欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android平台生成二维码并实现扫描 & 识别功能

程序员文章站 2024-03-31 18:07:22
1.二维码的前世今生 “二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数...

1.二维码的前世今生

“二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。 [1] ”

上面是百度百科的解释。既然有二维码,那么肯定有一维码。

一维码。最为常见的就是食品 & 书本后面的条码。

条码起源与20世纪40年代,后来在1970年 upc码发明,并开始广泛应用与食品包装。

具体的介绍可以看百度百科 一维码。

其实二维码与一维码本质上是类似的,就跟一维数组和二维数组一样。

2.二维码的java支持库

为了让java或者说android方便继承条码的功能,google就开发了一个zxing的库:

3.生成二维码

public class encodethread {
public static void encode(final string url, final int width, final int height, final encoderesult result) {
if (result == null) {
return;
}
if (textutils.isempty(url)) {
result.onencoderesult(null);
return;
}
new thread() {
@override
public void run() {
try {
multiformatwriter writer = new multiformatwriter();
hashtable<encodehinttype, string> hints = new hashtable<>();
hints.put(encodehinttype.character_set, "utf-8");
bitmatrix bitmatrix = writer.encode(url, barcodeformat.qr_code, width, height, hints);
bitmap bitmap = parsebitmatrix(bitmatrix);
result.onencoderesult(bitmap);
return;
} catch (writerexception e) {
e.printstacktrace();
}
result.onencoderesult(null);
}
}.start();
}
/**
* 生成二维码内容<br>
*
* @param matrix
* @return
*/
public static bitmap parsebitmatrix(bitmatrix matrix) {
final int qr_width = matrix.getwidth();
final int qr_height = matrix.getheight();
int[] pixels = new int[qr_width * qr_height];
//this we using qrcode algorithm
for (int y = 0; y < qr_height; y++) {
for (int x = 0; x < qr_width; x++) {
if (matrix.get(x, y)) {
pixels[y * qr_width + x] = 0xff000000;
} else {
pixels[y * qr_width + x] = 0xffffffff;
}
}
}
bitmap bitmap = bitmap.createbitmap(qr_width, qr_height, bitmap.config.argb_8888);
bitmap.setpixels(pixels, 0, qr_width, 0, 0, qr_width, qr_height);
return bitmap;
}
public interface encoderesult {
void onencoderesult(bitmap bitmap);
}
} 

zxing 支持很多条码格式:我们这里使用qr_code码。也就是我们常见的微信里面的二维码。

我们先来分析下这段代码:

multiformatwriter writer = new multiformatwriter();

这个是一个工具类,把所有支持的几个write写在里面了。

public bitmatrix encode(string contents,
barcodeformat format,
int width, int height,
map<encodehinttype,?> hints) throws writerexception {
writer writer;
switch (format) {
case ean_8:
writer = new ean8writer();
break;
case upc_e:
writer = new upcewriter();
break;
case ean_13:
writer = new ean13writer();
break;
case upc_a:
writer = new upcawriter();
break;
case qr_code:
writer = new qrcodewriter();
break;
case code_39:
writer = new code39writer();
break;
case code_93:
writer = new code93writer();
break;
case code_128:
writer = new code128writer();
break;
case itf:
writer = new itfwriter();
break;
case pdf_417:
writer = new pdf417writer();
break;
case codabar:
writer = new codabarwriter();
break;
case data_matrix:
writer = new datamatrixwriter();
break;
case aztec:
writer = new aztecwriter();
break;
default:
throw new illegalargumentexception("no encoder available for format " + format);
}
return writer.encode(contents, format, width, height, hints);
} 

这是官方最新支持的格式,具体看引入的jar里面支持的格式。

对与bitmatrix的结果,通过摸个算法,设置每个点白色,或者黑色。

最后创建一张二维码的图片。

4.识别二维码

如何从一张图片上面,识别二维码呢:

public class redecodethread {
public static void encode(final bitmap bitmap, final redecodethreadresult listener) {
if (listener == null) {
return;
}
if (bitmap == null) {
listener.onredecoderesult(null);
return;
}
new thread() {
@override
public void run() {
try {
multiformatreader multiformatreader = new multiformatreader();
bitmapluminancesource source = new bitmapluminancesource(bitmap);
binarybitmap bitmap1 = new binarybitmap(new hybridbinarizer(source));
result result1 = multiformatreader.decode(bitmap1);
listener.onredecoderesult(result1.gettext());
return;
} catch (notfoundexception e) {
e.printstacktrace();
}
listener.onredecoderesult(null);
}
}.start();
}
public interface redecodethreadresult {
void onredecoderesult(string url);
}
} 

过程也是很简单,使用multiformatreader来分析图片,这里不需要缺人图片的条码格式。

如果分析下源码,就是依次使用每种格式的reader来分析,直到找到合适的为止。

当然回了能够把bitmap转化成bitmatrix,然后在分析。

public final class bitmapluminancesource extends luminancesource{
private final byte[] luminances;
public bitmapluminancesource(string path) throws filenotfoundexception {
this(loadbitmap(path));
}
public bitmapluminancesource(bitmap bitmap) {
super(bitmap.getwidth(), bitmap.getheight());
int width = bitmap.getwidth();
int height = bitmap.getheight();
int[] pixels = new int[width * height];
bitmap.getpixels(pixels, 0, width, 0, 0, width, height);
// in order to measure pure decoding speed, we convert the entire image
// to a greyscale array
// up front, which is the same as the y channel of the
// yuvluminancesource in the real app.
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) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
if (r == g && g == b) {
// image is already greyscale, so pick any channel.
luminances[offset + x] = (byte) r;
} else {
// calculate luminance cheaply, favoring green.
luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
}
}
}
}
@override
public byte[] getrow(int y, byte[] row) {
if (y < 0 || y >= getheight()) {
throw new illegalargumentexception("requested row is outside the image: " + y);
}
int width = getwidth();
if (row == null || row.length < width) {
row = new byte[width];
}
system.arraycopy(luminances, y * width, row, 0, width);
return row;
}
// since this class does not support cropping, the underlying byte array
// already contains
// exactly what the caller is asking for, so give it to them without a copy.
@override
public byte[] getmatrix() {
return luminances;
}
private static bitmap loadbitmap(string path) throws filenotfoundexception {
bitmap bitmap = bitmapfactory.decodefile(path);
if (bitmap == null) {
throw new filenotfoundexception("couldn't open " + path);
}
return bitmap;
}
} 

5.扫描二维码

扫描二维码,其实比上面只多了一步,就是把camera获取的东西直接转换,然后进行识别。

public void requestpreviewframe(handler handler, int message) {
if (camera != null && previewing) {
previewcallback.sethandler(handler, message);
if (useoneshotpreviewcallback) {
camera.setoneshotpreviewcallback(previewcallback);
} else {
camera.setpreviewcallback(previewcallback);
}
}
} 

首先把camera预览的数据放入previewcallback中。

final class previewcallback implements camera.previewcallback 

public void onpreviewframe(byte[] data, camera camera) {
point cameraresolution = configmanager.getcameraresolution();
if (!useoneshotpreviewcallback) {
camera.setpreviewcallback(null);
}
if (previewhandler != null) {
message message = previewhandler.obtainmessage(previewmessage, cameraresolution.x,
cameraresolution.y, data);
message.sendtotarget();
previewhandler = null;
} else {
log.d(tag, "got preview callback, but no handler for it");
}
} 

可以看到,预览的数据data,回传递过来,然后handler的方式传递出去。

接收data的地方:

@override
public void handlemessage(message message) {
switch (message.what) {
case r.id.decode:
//log.d(tag, "got decode message");
decode((byte[]) message.obj, message.arg1, message.arg2);
break;
case r.id.quit:
looper.mylooper().quit();
break;
}
} 

然后是decode data

private void decode(byte[] data, int width, int height) {
long start = system.currenttimemillis();
result rawresult = null;
//modify here
byte[] rotateddata = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotateddata[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width; // here we are swapping, that's the difference to #11
width = height;
height = tmp;
planaryuvluminancesource source = cameramanager.get().buildluminancesource(rotateddata, width, height);
binarybitmap bitmap = new binarybitmap(new hybridbinarizer(source));
try {
rawresult = multiformatreader.decodewithstate(bitmap);
} catch (readerexception re) {
// continue
} finally {
multiformatreader.reset();
}
if (rawresult != null) {
long end = system.currenttimemillis();
log.d(tag, "found barcode (" + (end - start) + " ms):\n" + rawresult.tostring());
message message = message.obtain(activity.gethandler(), r.id.decode_succeeded, rawresult);
bundle bundle = new bundle();
bundle.putparcelable(decodethread.barcode_bitmap, source.rendercroppedgreyscalebitmap());
message.setdata(bundle);
//log.d(tag, "sending decode succeeded message...");
message.sendtotarget();
} else {
message message = message.obtain(activity.gethandler(), r.id.decode_failed);
message.sendtotarget();
}
} 

当把camera上的图片转换成binarybitmap以后,剩下的事情,就更直接从图片识别是一样的。

planaryuvluminancesource source = cameramanager.get().buildluminancesource(rotateddata, width, height);
binarybitmap bitmap = new binarybitmap(new hybridbinarizer(source));