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平台二维码之生成,扫描 & 识别的更多相关文章

  1. Android ZXing 二维码、条形码扫描介绍

    本帖最后由 Shims 于 2013-11-9 12:39 编辑 最近公司的Android项目需要用到摄像头做条码或二维码的扫描,Google一下,发现一个开源的 ZXing项目.它提供二维码和条形码 ...

  2. zxing二维码的生成与解码(C#)

    ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码.目标是能够对QR编码.Data Matrix.UPC的1D条形码进行解码. 其提供了多种平台下的客户端包括:J2ME.J2SE和An ...

  3. Android实例-实现扫描二维码并生成二维码(XE8+小米5)

    相关资料: 第三方资料太大没法写在博文上,请下载CSDN的程序包. 程序包下载: http://download.csdn.net/detail/zhujianqiangqq/9657186 注意事项 ...

  4. 玩转Android之二维码生成与识别

    二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了.那么今天我们就来看看怎么样在我们自己的App中集成二维码的 ...

  5. Android集成二维码扫描功能

    文章转载自  https://github.com/yipianfengye/android-zxingLibrary 在具体介绍该扫描库之前我们先看一下其具体的使用方式,看看是不是几行代码就可以集成 ...

  6. android开发之集成zxing,二维码,以及扫描二维码的功能实现。带源代码下载

    package cc.jiusansec.www; import com.google.zxing.WriterException; import com.zxing.activity.Capture ...

  7. Android—ZXing二维码扫描遇到的问题

    最近工作中需要开发带有二维码扫描功能的软件(基于开源项目ZXing),遇到的问题记录一下,也希望给大家带来帮助. 1.首先因为扫描要开摄像机所以加权限是一定的,不然后面什么都不能进行 <uses ...

  8. Android zxing 解析二维码,生成二维码极简demo

    zxing 官方的代码很多,看起来很费劲,此demo只抽取了有用的部分,实现了相机预览解码,解析本地二维码,生成二维码三个功能. 简化后的结构如下: 废话少说直接上代码: BaseDecodeHand ...

  9. Swift开发小技巧--扫描二维码,二维码的描边与锁定,设置扫描范围,二维码的生成(高清,无码,你懂得!)

    二维码的扫描,二维码的锁定与描边,二维码的扫描范围,二维码的生成(高清,无码,你懂得!),识别相册中的二维码 扫描二维码用到的三个重要对象的关系,如图: 1.懒加载各种类 // MARK: - 懒加载 ...

随机推荐

  1. 用JavaScript修改浏览器tab标题

    修改tab或者window的标题,是一项较老的实践.Gmail 用它来提示用户新的聊天消息,当有新的page通过AJAX加载的时候,本站同样用它更新tab title.这是怎样做到的呢?当时是通过设置 ...

  2. 基于tiny4412的Linux内核移植 -- 设备树的展开

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  3. LeetCode——Find Median from Data Stream

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  4. notepad++ 各类插件学习记录

    js文件的规范格式排列: 插件里安装 jstools, 然后重启notepad++再去插件里面的jstool里面用jsformat就可以格式化排列不规则的js代码了 notepad++ 自动补全: 在 ...

  5. Java魔法堂:注解用法详解——@SuppressWarnings

    一.前言 编码时我们总会发现如下变量未被使用的警告提示: 上述代码编译通过且可以运行,但每行前面的“感叹号”就严重阻碍了我们判断该行是否设置的断点了.这时我们可以在方法前添加 @SuppressWar ...

  6. Eclipse启动报错:A java runtime Environment(JRE) or java Development……的解决办法

    第一种: 解决方法: 系统变量里设置下面: 变量名:JAVA_HOME 变量值:D:\Java\jdk1.8.0_31 变量名:CLASSPATH 变量值:.;%JAVA_HOME%\lib; 变量名 ...

  7. 自制奇葩vb面试题,看你能对几道

    这些题都比较奇葩,所以做出选择之前请仔细考虑. 答题过程中不要离开当前页面,不要去试代码,也不要查参考或问别人. 转载请说明作者是 Nukepayload2 Vb版本:14 默认的.net frame ...

  8. 【C#进阶系列】09 关于参数的故事

    可选参数和命名参数 不多说,上代码,自然懂 class Program { static void Main(string[] args) { var troy = new Troy(); troy. ...

  9. Linux FTP配置文件说明

    一.vsftpd说明: LINUX下实现FTP服务的软件很多,最常见的有vsftpd,Wu-ftpd和Proftp等.Red Hat Enterprise Linux中默认安装的是vsftpd. 访问 ...

  10. 推荐轻量友好的.NET测试断言工具Shouldly

    Shouldly是一个轻量的断言(Assertion)框架,用于补充.NET框架下的测试工具.Shouldly将焦点放在当断言失败时如何简单精准的给出很好的错误信息. Shouldly在GitHub的 ...