Android平台二维码之生成,扫描 & 识别
1.二维码的前世今生
“二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。 [1] ”
上面是百度百科的解释。既然有二维码,那么肯定有一维码。
一维码。最为常见的就是食品 & 书本后面的条码。
条码起源与20世纪40年代,后来在1970年 UPC码发明,并开始广泛应用与食品包装。
具体的介绍可以看百度百科 一维码。
其实二维码与一维码本质上是类似的,就跟一维数组和二维数组一样。
2.二维码的java支持库
为了让java或者说android方便继承条码的功能,google就开发了一个zxing的库:
https://github.com/zxing/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;
}
}
BitmapLuminanceSource
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));
参考:
http://www.cnblogs.com/weixing/archive/2013/08/28/3287120.html
Android平台二维码之生成,扫描 & 识别的更多相关文章
- Android ZXing 二维码、条形码扫描介绍
本帖最后由 Shims 于 2013-11-9 12:39 编辑 最近公司的Android项目需要用到摄像头做条码或二维码的扫描,Google一下,发现一个开源的 ZXing项目.它提供二维码和条形码 ...
- zxing二维码的生成与解码(C#)
ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码.目标是能够对QR编码.Data Matrix.UPC的1D条形码进行解码. 其提供了多种平台下的客户端包括:J2ME.J2SE和An ...
- Android实例-实现扫描二维码并生成二维码(XE8+小米5)
相关资料: 第三方资料太大没法写在博文上,请下载CSDN的程序包. 程序包下载: http://download.csdn.net/detail/zhujianqiangqq/9657186 注意事项 ...
- 玩转Android之二维码生成与识别
二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了.那么今天我们就来看看怎么样在我们自己的App中集成二维码的 ...
- Android集成二维码扫描功能
文章转载自 https://github.com/yipianfengye/android-zxingLibrary 在具体介绍该扫描库之前我们先看一下其具体的使用方式,看看是不是几行代码就可以集成 ...
- android开发之集成zxing,二维码,以及扫描二维码的功能实现。带源代码下载
package cc.jiusansec.www; import com.google.zxing.WriterException; import com.zxing.activity.Capture ...
- Android—ZXing二维码扫描遇到的问题
最近工作中需要开发带有二维码扫描功能的软件(基于开源项目ZXing),遇到的问题记录一下,也希望给大家带来帮助. 1.首先因为扫描要开摄像机所以加权限是一定的,不然后面什么都不能进行 <uses ...
- Android zxing 解析二维码,生成二维码极简demo
zxing 官方的代码很多,看起来很费劲,此demo只抽取了有用的部分,实现了相机预览解码,解析本地二维码,生成二维码三个功能. 简化后的结构如下: 废话少说直接上代码: BaseDecodeHand ...
- Swift开发小技巧--扫描二维码,二维码的描边与锁定,设置扫描范围,二维码的生成(高清,无码,你懂得!)
二维码的扫描,二维码的锁定与描边,二维码的扫描范围,二维码的生成(高清,无码,你懂得!),识别相册中的二维码 扫描二维码用到的三个重要对象的关系,如图: 1.懒加载各种类 // MARK: - 懒加载 ...
随机推荐
- Java Annotation认知(包括框架图、详细介绍、示例说明)
摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...
- python异常处理(基础)
之前在学习python的时候有整理过python异常处理的文章,不够简单也不够完整,所以决定再整理一篇,算做补充. http://www.cnblogs.com/fnng/archive/2013/0 ...
- Java集合框架源码剖析:LinkedHashSet 和 LinkedHashMap
Java LinkedHashMap和HashMap有什么区别和联系?为什么LinkedHashMap会有着更快的迭代速度?LinkedHashSet跟LinkedHashMap有着怎样的内在联系?本 ...
- 连续值的CART(分类回归树)原理和实现
上一篇我们学习和实现了CART(分类回归树),不过主要是针对离散值的分类实现,下面我们来看下连续值的cart分类树如何实现 思考连续值和离散值的不同之处: 二分子树的时候不同:离散值需要求出最优的两个 ...
- Water --- CSU 1550: Simple String
Simple String Problem's Link: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1550 Mean: 略. analy ...
- 【循序渐进学Python】10.模块和包
1.导入模块 任何Python程序都可以作为模块导入,只要Python解释器能找到我们定义的模块所在位置即可,一般来讲,在一个模块被导入时,Python解释器会按照下面的步骤进行搜索: 在当前所在目录 ...
- 从web页面启动winform程序的实现方法
本文实现的需求是: A.通过web页面启动winform程序: B.将页面的参数传递给winform程序: C.winform程序已经启动并正在运行时,从web页面不能重新启动winform程序,只是 ...
- C#根据CPU+磁盘标号来注册软件
很多私人软件都需要自己的作品出售给别人只能一台电脑使用,不可以随便一个电脑都可以运行自己的软件,所以就有了软件注册限制的控制,收集了一个注册软件的帮助类,分享记录一下. 功能介绍: 根据CPU+ ...
- javascript类的理解和使用
距离上次写博客已经过去好几个月了,现在手里的项目正好都结束了,闲暇之后开始理一下开发中一些问题,这次说一下javascript当中的类,可能很多人对于写惯了前台页面效果的coder来说,对于javas ...
- PostgreSQL avg()函数
PostgreSQL的AVG函数是用来找出各种记录中的一个字段的平均值. 为了理解AVG函数考虑表COMPANY 有如下记录: testdb# select * from COMPANY; id | ...