一、生成二维码
生成二维码比较简单,ZXing官方的demo中也有该功能,直接贴出提取的代码:
/** * 生成二维码图片 * * @param str * @return */ public static Bitmap createBarcode(String str) { Bitmap bitmap = null; BitMatrix result = null; MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); try { result = multiFormatWriter.encode(str, BarcodeFormat.QR_CODE, 200, 200); int w = result.getWidth(); int h = result.getHeight(); int[] pixels = new int[w * h]; for (int y = 0; y < h; y++) { int offset = y * w; for (int x = 0; x < w; x++) { pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; } } bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, w, 0, 0, w, h); LogUtils.d(TAG + "-w-" + w); LogUtils.d(TAG + "-h-" + h); LogUtils.d(TAG + "-width-" + bitmap.getWidth()); LogUtils.d(TAG + "-height-" + bitmap.getHeight()); } catch (WriterException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } return bitmap; }
二、识别本地二维码图片
在实现该功能时,走了写弯路;一开始呢,我是计划着根据用摄像头扫描二维码的功能,抽取出最后识别图片的方法,最终找到了DecodeHandler类下的方法:
private void decode(byte[] data, int width, int height)
本以为使用该方法,将图片转化成byte数组就可以了,实时证明想的太简单了,无法识别二维码。
后来就百度下,发现网上有两种实现方式,一种基于PlanarYUVLuminanceSource,另一种RGBLuminanceSource,这是两种图片数据编码方式(YUV和RGB)的不同解析方法。而DecodeHandler下的decode方法中的使用就是基于PlanarYUVLuminanceSource的实现,这就说明参数byte数组data,不是简单的将bitmap转换成byte[]数组这么简单,而是需要先将bitmap转换成像素数组int[] pixel,再将像素数组int[] pixel根据RGB转YUV公式:
Y=0.299R+0.587G+0.114B; U=-0.147R-0.289G+0.436B; V=0.615R-0.515G-0.1B;转换成byte[] yuv,而这个byte数组yuv才是真正用来传入DecodeHandler下decode方法的参数;
下面贴出分别基于RGB和YUV两种方式的识别本地二维码图片的方法:
1、基于RGB方式
/** * 解析二维码(使用解析RGB编码数据的方式) * * @param path * @return */ public static Result decodeBarcodeRGB(String path) { if (TextUtils.isEmpty(path)) { return null; } BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 1; Bitmap barcode = BitmapFactory.decodeFile(path, opts); Result result = decodeBarcodeRGB(barcode); barcode.recycle(); barcode = null; return result; } /** * 解析二维码 (使用解析RGB编码数据的方式) * * @param barcode * @return */ public static Result decodeBarcodeRGB(Bitmap barcode) { int width = barcode.getWidth(); int height = barcode.getHeight(); int[] data = new int[width * height]; barcode.getPixels(data, 0, width, 0, 0, width, height); RGBLuminanceSource source = new RGBLuminanceSource(width, height, data); BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source)); QRCodeReader reader = new QRCodeReader(); Result result = null; try { result = reader.decode(bitmap1); } catch (NotFoundException e) { e.printStackTrace(); } catch (ChecksumException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } barcode.recycle(); barcode = null; return result; }
/** * 解析二维码(使用解析YUV编码数据的方式) * * @param path * @return */ public static Result decodeBarcodeYUV(String path) { if (TextUtils.isEmpty(path)) { return null; } BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 1; Bitmap barcode = BitmapFactory.decodeFile(path, opts); Result result = decodeBarcodeYUV(barcode); barcode.recycle(); barcode = null; return result; } /** * 解析二维码(使用解析YUV编码数据的方式) * * @param barcode * @return */ public static Result decodeBarcodeYUV(Bitmap barcode) { if (null == barcode) { return null; } int width = barcode.getWidth(); int height = barcode.getHeight(); //以argb方式存放图片的像素 int[] argb = new int[width * height]; barcode.getPixels(argb, 0, width, 0, 0, width, height); //将argb转换为yuv byte[] yuv = new byte[width * height * 3 / 2]; encodeYUV420SP(yuv, argb, width, height); //解析YUV编码方式的二维码 Result result = decodeBarcodeYUV(yuv, width, height); barcode.recycle(); barcode = null; return result; } /** * 解析二维码(使用解析YUV编码数据的方式) * * @param yuv * @param width * @param height * @return */ private static Result decodeBarcodeYUV(byte[] yuv, int width, int height) { long start = System.currentTimeMillis(); MultiFormatReader multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(null); Result rawResult = null; PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(yuv, width, height, 0, 0, width, height, false); if (source != null) { BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { re.printStackTrace(); } finally { multiFormatReader.reset(); multiFormatReader = null; } } long end = System.currentTimeMillis(); LogUtils.d(TAG + " --barcode decode in " + (end - start) + " ms"); return rawResult; } /** * RGB转YUV的公式是: * Y=0.299R+0.587G+0.114B; * U=-0.147R-0.289G+0.436B; * V=0.615R-0.515G-0.1B; * * @param yuv * @param argb * @param width * @param height */ private static void encodeYUV420SP(byte[] yuv, 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 R, G, B; int rgbIndex = 0; // ---循环所有像素点,RGB转YUV--- for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { R = (argb[rgbIndex] & 0xff0000) >> 16; G = (argb[rgbIndex] & 0xff00) >> 8; B = (argb[rgbIndex] & 0xff); // rgbIndex++; // 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 scan line. // ---Y--- yuv[yIndex++] = (byte) Y; // ---UV--- if ((j % 2 == 0) && (i % 2 == 0)) { // yuv[uvIndex++] = (byte) V; // yuv[uvIndex++] = (byte) U; } } } }
注:对于YUV和RGB的理解, 引用知乎上看到的资料:
作者:祥子 链接:https://www.zhihu.com/question/56384589/answer/154035486 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 Android相机预览的时候支持几种不同的格式,从图像的角度(ImageFormat)来说有NV16、NV21、YUY2、YV12、RGB_565和JPEG,从像素的角度(PixelFormat)来说,有YUV422SP、YUV420SP、YUV422I、YUV420P、RGB565和JPEG,它们之间的对应关系可以从Camera.Parameters.cameraFormatForPixelFormat(int) 方法中得到。 针对YUV编码的数据,有PlanarYUVLuminanceSource这个类去处理,而针对RGB编码的数据,则用RGBLuminanceSource去处理。大部分二维码的识别都是基于二值化的方法,在色域的处理上,YUV的二值化效果要优于RGB, 而我们的摄像头,或者从相册取到的图片,都是RGB图像, RGB的缺点很明显,不直观,不均匀,对设备还有依赖性。采用YUV色彩空间的重要性是它的信号亮度Y和色度亮度U,V是分离的;RGB转YUV的公式是:Y=0.299R+0.587G+0.114B;U=-0.147R-0.289G+0.436B;V=0.615R-0.515G-0.1B;