Android开源库之使用ZXing开源库生成二维码及识别本地二维码图片

时间:2022-11-17 07:25:57

一、生成二维码

生成二维码比较简单,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;
}

2、基于YUV方法
/**     * 解析二维码(使用解析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;