Android 解析二维码图片的两种方法介绍
做了一个二维码扫描图片,主要是扫描不出来,看到一篇博客,其中的第二种方法可以扫描到,在此做笔记,以备后用,前面的进入相册,返回,到获取图片路径方法都一样;
1.项目需求:知道项目需求,才知道先从哪里入手,见图一。(点击相册,打开图库)
2.代码:
1.打开图库代码:
mbtnopenpicture.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { //打开相册 opengallery(); } }); /**打开相册*/ private void opengallery() { intent picture = new intent(intent.action_pick,android.provider.mediastore.images.media.external_content_uri); startactivityforresult(picture, picture); }
2.获取图片路径和解析。
(1). 获取图片路径:getpath(uri);由于android版本不同,返回的uri会有所不同,需要做特殊处理。
@suppresslint("newapi") private string getpath(uri uri) { int sdkversion = build.version.sdk_int; if (sdkversion >= 19) { //log.e("hxy", "uri auth: " + uri.getauthority()); if (isexternalstoragedocument(uri)) { string docid = documentscontract.getdocumentid(uri); string[] split = docid.split(":"); string type = split[0]; if ("primary".equalsignorecase(type)) { return environment.getexternalstoragedirectory() + "/" + split[1]; } } else if (isdownloadsdocument(uri)) { final string id = documentscontract.getdocumentid(uri); final uri contenturi = contenturis.withappendedid(uri.parse("content://downloads/public_downloads"), long.valueof(id)); return getdatacolumn(this, contenturi, null, null); } else if (ismediadocument(uri)) { final string docid = documentscontract.getdocumentid(uri); final string[] split = docid.split(":"); final string type = split[0]; uri contenturi = null; if ("image".equals(type)) { contenturi = mediastore.images.media.external_content_uri; } else if ("video".equals(type)) { contenturi = mediastore.video.media.external_content_uri; } else if ("audio".equals(type)) { contenturi = mediastore.audio.media.external_content_uri; } final string selection = "_id="; final string[] selectionargs = new string[]{split[1]}; return getdatacolumn(this, contenturi, selection, selectionargs); } else if (ismedia(uri)) { string[] proj = {mediastore.images.media.data}; cursor actualimagecursor = this.managedquery(uri, proj, null, null, null); int actual_image_column_index = actualimagecursor.getcolumnindexorthrow(mediastore.images.media.data); actualimagecursor.movetofirst(); return actualimagecursor.getstring(actual_image_column_index); } } else if ("content".equalsignorecase(uri.getscheme())) { if (isgooglephotosuri(uri)) return uri.getlastpathsegment(); return getdatacolumn(this, uri, null, null); } // file else if ("file".equalsignorecase(uri.getscheme())) { return uri.getpath(); } return null; } public static string getdatacolumn(context context, uri uri, string selection, string[] selectionargs) { cursor cursor = null; final string column = "_data"; final string[] projection = { column }; try { cursor = context.getcontentresolver().query(uri, projection, selection, selectionargs, null); if (cursor != null && cursor.movetofirst()) { final int column_index = cursor.getcolumnindexorthrow(column); return cursor.getstring(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } private static boolean isexternalstoragedocument(uri uri) { return "com.android.externalstorage.documents".equals(uri.getauthority()); } public static boolean isdownloadsdocument(uri uri) { return "com.android.providers.downloads.documents".equals(uri.getauthority()); } public static boolean ismediadocument(uri uri) { return "com.android.providers.media.documents".equals(uri.getauthority()); } public static boolean ismedia(uri uri) { return "media".equals(uri.getauthority()); } public static boolean isgooglephotosuri(uri uri) { return "com.google.android.apps.photos.content".equals(uri.getauthority()); }
(2).开辟线程,解析图片,封装到result中:我看了网上的demo,大致上有2种方法。如下是第一种,稍后会把第二种方法也贴出来。
/** * 解析二维码图片 * @param path * @return */ protected result scanningimage(string path) { if (textutils.isempty(path)) { return null; } hashtable hints = new hashtable(); hints.put(decodehinttype.character_set, "utf-8"); // 设置二维码内容的编码 bitmapfactory.options options = new bitmapfactory.options(); options.injustdecodebounds = true; // 先获取原大小 scanbitmap = bitmapfactory.decodefile(path,options); options.injustdecodebounds = false; int samplesize = (int) (options.outheight / (float) 200); if (samplesize <= 0) samplesize = 1; options.insamplesize = samplesize; scanbitmap = bitmapfactory.decodefile(path, options); int[] data = new int[scanbitmap.getwidth() * scanbitmap.getheight()]; scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), scanbitmap.getheight()); rgbluminancesource rgbluminancesource = new rgbluminancesource(scanbitmap.getwidth(),scanbitmap.getheight(),data); binarybitmap binarybitmap = new binarybitmap(new hybridbinarizer(rgbluminancesource)); qrcodereader reader = new qrcodereader(); result result = null; try { result = reader.decode(binarybitmap, hints); } catch (notfoundexception e) { log.e("hxy","notfoundexception"); }catch (checksumexception e){ log.e("hxy","checksumexception"); }catch(formatexception e){ log.e("hxy","formatexception"); } return result; }
注意:上面方法中有个关键的类,rgbluminancesource。 我把这个类的构造方法贴出来,因为我做的时候,发现网上demo 这个类中构造传递的都是bitmap, 而我这个类却不是。分析传递的参数之后,我做了个转化:见如下,然后会发现报:notfoundexception. 这个异常是在qrcodereader类:private static bitmatrix extractpurebits(bitmatrix image) throws notfoundexception 。(到这里我已经不是很懂了,然后又去网上搜索了下,最后自己探索出加scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), scanbitmap.getheight());),运行正常,能解析出来。
int[] data = new int[scanbitmap.getwidth() * scanbitmap.getheight()]; //一定要加以下这个代码: //scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), //scanbitmap.getheight()); rgbluminancesource rgbluminancesource = new rgbluminancesource(scanbitmap.getwidth(),scanbitmap.getheight(),data);
public rgbluminancesource(int width, int height, int[] pixels) { super(width, height); this.datawidth = width; this.dataheight = height; this.left = 0; this.top = 0; this.luminances = new byte[width * height]; for(int y = 0; y < height; ++y) { int offset = y * width; for(int x = 0; x < width; ++x) { int pixel = pixels[offset + x]; int r = pixel >> 16 & 255; int g = pixel >> 8 & 255; int b = pixel & 255; if(r == g && g == b) { this.luminances[offset + x] = (byte)r; } else { this.luminances[offset + x] = (byte)((r + 2 * g + b) / 4); } } } }
现在来看第二种解析方法:
protected result scanningimage(string path) { if (textutils.isempty(path)) { return null; } bitmapfactory.options options = new bitmapfactory.options(); options.injustdecodebounds = true; // 先获取原大小 scanbitmap = bitmapfactory.decodefile(path,options); options.injustdecodebounds = false; int samplesize = (int) (options.outheight / (float) 200); if (samplesize <= 0) samplesize = 1; options.insamplesize = samplesize; scanbitmap = bitmapfactory.decodefile(path, options); byte[] data = getyuv420sp(scanbitmap.getwidth(), scanbitmap.getheight(), scanbitmap); hashtable hints = new hashtable(); hints.put(decodehinttype.character_set, "utf-8"); // 设置二维码内容的编码 hints.put(decodehinttype.try_harder,boolean.true); hints.put(decodehinttype.possible_formats, barcodeformat.qr_code); planaryuvluminancesource source = new planaryuvluminancesource(data, scanbitmap.getwidth(), scanbitmap.getheight(), 0, 0, scanbitmap.getwidth(), scanbitmap.getheight(), false); binarybitmap bitmap1 = new binarybitmap(new hybridbinarizer(source)); qrcodereader reader2= new qrcodereader(); result result = null; try { result = reader2.decode(bitmap1, hints); log.e("hxy",result.gettext()); } catch (notfoundexception e) { log.e("hxy","notfoundexception"); }catch (checksumexception e){ log.e("hxy","checksumexception"); }catch(formatexception e){ log.e("hxy","formatexception"); } return result; } public byte[] getyuv420sp(int inputwidth, int inputheight, bitmap scaled) { int[] argb = new int[inputwidth * inputheight]; scaled.getpixels(argb, 0, inputwidth, 0, 0, inputwidth, inputheight); byte[] yuv = new byte[inputwidth * inputheight * 3 / 2]; encodeyuv420sp(yuv, argb, inputwidth, inputheight); scaled.recycle(); return yuv; } private void encodeyuv420sp(byte[] yuv420sp, int[] argb, int width, int height) { // 帧图片的像素大小 final int framesize = width * height; // ---yuv数据--- int y, u, v; // y的index从0开始 int yindex = 0; // uv的index从framesize开始 int uvindex = framesize; // ---颜色数据--- //int a, r, g, b; int r, g, b; // int argbindex = 0; // // ---循环所有像素点,rgb转yuv--- for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { // a is not used obviously // a = (argb[argbindex] & 0xff000000) >> 24; r = (argb[argbindex] & 0xff0000) >> 16; g = (argb[argbindex] & 0xff00) >> 8; b = (argb[argbindex] & 0xff); // argbindex++; // well known rgb to yuv algorithm y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; // y = math.max(0, math.min(y, 255)); u = math.max(0, math.min(u, 255)); v = math.max(0, math.min(v, 255)); // nv21 has a plane of y and interleaved planes of vu each // sampled by a factor of 2 // meaning for every 4 y pixels there are 1 v and 1 u. note the // sampling is every other // pixel and every other scanline. // ---y--- yuv420sp[yindex++] = (byte) y; // ---uv--- // if ((j % 2 == 0) && (i % 2 == 0)) { // // // //yuv420sp[uvindex++] = (byte) v; // //yuv420sp[uvindex++] = (byte) u; // } } } }
最后2行代码在实际运行的时候,如果是拿一个二维码图片,能正常解析,但是如果不是二维码图片,数组越界。然后我将其注释掉之后,一切正常了。在这里的转化,我没有看懂,只是提供一种解析方案,期待对这方面了解之人能我和探讨。
3.将解析结果回调给调用activity.:
@override protected void onactivityresult(int requestcode, int resultcode, intent data) { if(resultcode==result_ok&&data != null&&requestcode == picture){ uri selectedimage = data.getdata(); final string pathresult = getpath(selectedimage); log.e("hxy","pathresult:"+pathresult); new thread(new runnable() { @override public void run() { result result = scanningimage(pathresult); if(result==null){ looper.prepare(); toast.maketext(captureactivity.this, "未识别到二维码",toast.length_long) .show(); looper.loop(); }else{ handledecode(result,new bundle()); // string recode = recode(result.tostring()); // log.e("hxy","recode:"+recode); // intent data = new intent(); // data.putextra("result", recode); // setresult(300, data); //finish(); } } }).start(); } }
在这里调用handledecode。是由于captureactivity中,将结果就是通过handledecode回调的
/** * a valid barcode has been found, so give an indication of success and show * the results. * * @param rawresult *the contents of the barcode. * * @param bundle *the extras */ public void handledecode(result rawresult, bundle bundle) { inactivitytimer.onactivity(); beepmanager.playbeepsoundandvibrate(); bundle.putint("width", mcroprect.width()); bundle.putint("height", mcroprect.height()); bundle.putstring("result", rawresult.gettext()); //startactivity(new intent(captureactivity.this, resultactivity.class).putextras(bundle)); setresult(result_ok, new intent().putextras(bundle)); //toast.maketext(this, rawresult.gettext(), toast.length_long); finish(); }