1.二维码的前世今生

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

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

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

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

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

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

2.二维码的java支持库

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

https://github.com/zxing/zxing

3.生成二维码

  1. public class EncodeThread {
  2.  
  3. public static void encode(final String url, final int width, final int height, final EncodeResult result) {
  4.  
  5. if (result == null) {
  6. return;
  7. }
  8.  
  9. if (TextUtils.isEmpty(url)) {
  10. result.onEncodeResult(null);
  11. return;
  12. }
  13.  
  14. new Thread() {
  15. @Override
  16. public void run() {
  17. try {
  18. MultiFormatWriter writer = new MultiFormatWriter();
  19. Hashtable<EncodeHintType, String> hints = new Hashtable<>();
  20. hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
  21. BitMatrix bitMatrix = writer.encode(url, BarcodeFormat.QR_CODE, width, height, hints);
  22. Bitmap bitmap = parseBitMatrix(bitMatrix);
  23. result.onEncodeResult(bitmap);
  24. return;
  25. } catch (WriterException e) {
  26. e.printStackTrace();
  27. }
  28. result.onEncodeResult(null);
  29. }
  30. }.start();
  31.  
  32. }
  33.  
  34. /**
  35. * 生成二维码内容<br>
  36. *
  37. * @param matrix
  38. * @return
  39. */
  40. public static Bitmap parseBitMatrix(BitMatrix matrix) {
  41. final int QR_WIDTH = matrix.getWidth();
  42. final int QR_HEIGHT = matrix.getHeight();
  43. int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
  44. //this we using qrcode algorithm
  45. for (int y = 0; y < QR_HEIGHT; y++) {
  46. for (int x = 0; x < QR_WIDTH; x++) {
  47. if (matrix.get(x, y)) {
  48. pixels[y * QR_WIDTH + x] = 0xff000000;
  49. } else {
  50. pixels[y * QR_WIDTH + x] = 0xffffffff;
  51. }
  52. }
  53. }
  54. Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
  55. bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
  56. return bitmap;
  57. }
  58.  
  59. public interface EncodeResult {
  60. void onEncodeResult(Bitmap bitmap);
  61. }
  62. }

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

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

  1. MultiFormatWriter writer = new MultiFormatWriter();

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

  1. public BitMatrix encode(String contents,
  2. BarcodeFormat format,
  3. int width, int height,
  4. Map<EncodeHintType,?> hints) throws WriterException {
  5.  
  6. Writer writer;
  7. switch (format) {
  8. case EAN_8:
  9. writer = new EAN8Writer();
  10. break;
  11. case UPC_E:
  12. writer = new UPCEWriter();
  13. break;
  14. case EAN_13:
  15. writer = new EAN13Writer();
  16. break;
  17. case UPC_A:
  18. writer = new UPCAWriter();
  19. break;
  20. case QR_CODE:
  21. writer = new QRCodeWriter();
  22. break;
  23. case CODE_39:
  24. writer = new Code39Writer();
  25. break;
  26. case CODE_93:
  27. writer = new Code93Writer();
  28. break;
  29. case CODE_128:
  30. writer = new Code128Writer();
  31. break;
  32. case ITF:
  33. writer = new ITFWriter();
  34. break;
  35. case PDF_417:
  36. writer = new PDF417Writer();
  37. break;
  38. case CODABAR:
  39. writer = new CodaBarWriter();
  40. break;
  41. case DATA_MATRIX:
  42. writer = new DataMatrixWriter();
  43. break;
  44. case AZTEC:
  45. writer = new AztecWriter();
  46. break;
  47. default:
  48. throw new IllegalArgumentException("No encoder available for format " + format);
  49. }
  50. return writer.encode(contents, format, width, height, hints);
  51. }

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

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

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

4.识别二维码

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

  1. public class ReDecodeThread {
  2.  
  3. public static void encode(final Bitmap bitmap, final ReDecodeThreadResult listener) {
  4.  
  5. if (listener == null) {
  6. return;
  7. }
  8.  
  9. if (bitmap == null) {
  10. listener.onReDecodeResult(null);
  11. return;
  12. }
  13.  
  14. new Thread() {
  15. @Override
  16. public void run() {
  17. try {
  18. MultiFormatReader multiFormatReader = new MultiFormatReader();
  19. BitmapLuminanceSource source = new BitmapLuminanceSource(bitmap);
  20. BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
  21. Result result1 = multiFormatReader.decode(bitmap1);
  22. listener.onReDecodeResult(result1.getText());
  23. return;
  24. } catch (NotFoundException e) {
  25. e.printStackTrace();
  26. }
  27. listener.onReDecodeResult(null);
  28. }
  29. }.start();
  30.  
  31. }
  32.  
  33. public interface ReDecodeThreadResult {
  34. void onReDecodeResult(String url);
  35. }
  36. }

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

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

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

  1. public final class BitmapLuminanceSource extends LuminanceSource{
  2. private final byte[] luminances;
  3.  
  4. public BitmapLuminanceSource(String path) throws FileNotFoundException {
  5. this(loadBitmap(path));
  6. }
  7.  
  8. public BitmapLuminanceSource(Bitmap bitmap) {
  9. super(bitmap.getWidth(), bitmap.getHeight());
  10.  
  11. int width = bitmap.getWidth();
  12. int height = bitmap.getHeight();
  13.  
  14. int[] pixels = new int[width * height];
  15. bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
  16.  
  17. // In order to measure pure decoding speed, we convert the entire image
  18. // to a greyscale array
  19. // up front, which is the same as the Y channel of the
  20. // YUVLuminanceSource in the real app.
  21. luminances = new byte[width * height];
  22. for (int y = 0; y < height; y++) {
  23. int offset = y * width;
  24. for (int x = 0; x < width; x++) {
  25. int pixel = pixels[offset + x];
  26. int r = (pixel >> 16) & 0xff;
  27. int g = (pixel >> 8) & 0xff;
  28. int b = pixel & 0xff;
  29. if (r == g && g == b) {
  30. // Image is already greyscale, so pick any channel.
  31. luminances[offset + x] = (byte) r;
  32. } else {
  33. // Calculate luminance cheaply, favoring green.
  34. luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
  35. }
  36. }
  37. }
  38. }
  39.  
  40. @Override
  41. public byte[] getRow(int y, byte[] row) {
  42. if (y < 0 || y >= getHeight()) {
  43. throw new IllegalArgumentException("Requested row is outside the image: " + y);
  44. }
  45. int width = getWidth();
  46. if (row == null || row.length < width) {
  47. row = new byte[width];
  48. }
  49.  
  50. System.arraycopy(luminances, y * width, row, 0, width);
  51. return row;
  52. }
  53.  
  54. // Since this class does not support cropping, the underlying byte array
  55. // already contains
  56. // exactly what the caller is asking for, so give it to them without a copy.
  57. @Override
  58. public byte[] getMatrix() {
  59. return luminances;
  60. }
  61.  
  62. private static Bitmap loadBitmap(String path) throws FileNotFoundException {
  63. Bitmap bitmap = BitmapFactory.decodeFile(path);
  64. if (bitmap == null) {
  65. throw new FileNotFoundException("Couldn't open " + path);
  66. }
  67. return bitmap;
  68. }
  69. }

BitmapLuminanceSource

5.扫描二维码

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

  1. public void requestPreviewFrame(Handler handler, int message) {
  2. if (camera != null && previewing) {
  3. previewCallback.setHandler(handler, message);
  4. if (useOneShotPreviewCallback) {
  5. camera.setOneShotPreviewCallback(previewCallback);
  6. } else {
  7. camera.setPreviewCallback(previewCallback);
  8. }
  9. }
  10. }

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

  1. final class PreviewCallback implements Camera.PreviewCallback
  1. public void onPreviewFrame(byte[] data, Camera camera) {
  2. Point cameraResolution = configManager.getCameraResolution();
  3. if (!useOneShotPreviewCallback) {
  4. camera.setPreviewCallback(null);
  5. }
  6. if (previewHandler != null) {
  7. Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
  8. cameraResolution.y, data);
  9. message.sendToTarget();
  10. previewHandler = null;
  11. } else {
  12. Log.d(TAG, "Got preview callback, but no handler for it");
  13. }
  14. }

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

接收data的地方:

  1. @Override
  2. public void handleMessage(Message message) {
  3. switch (message.what) {
  4. case R.id.decode:
  5. //Log.d(TAG, "Got decode message");
  6. decode((byte[]) message.obj, message.arg1, message.arg2);
  7. break;
  8. case R.id.quit:
  9. Looper.myLooper().quit();
  10. break;
  11. }
  12. }

然后是decode data

  1. private void decode(byte[] data, int width, int height) {
  2. long start = System.currentTimeMillis();
  3. Result rawResult = null;
  4.  
  5. //modify here
  6. byte[] rotatedData = new byte[data.length];
  7. for (int y = 0; y < height; y++) {
  8. for (int x = 0; x < width; x++)
  9. rotatedData[x * height + height - y - 1] = data[x + y * width];
  10. }
  11. int tmp = width; // Here we are swapping, that's the difference to #11
  12. width = height;
  13. height = tmp;
  14.  
  15. PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
  16. BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
  17. try {
  18. rawResult = multiFormatReader.decodeWithState(bitmap);
  19. } catch (ReaderException re) {
  20. // continue
  21. } finally {
  22. multiFormatReader.reset();
  23. }
  24.  
  25. if (rawResult != null) {
  26. long end = System.currentTimeMillis();
  27. Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
  28. Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
  29. Bundle bundle = new Bundle();
  30. bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
  31. message.setData(bundle);
  32. //Log.d(TAG, "Sending decode succeeded message...");
  33. message.sendToTarget();
  34. } else {
  35. Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
  36. message.sendToTarget();
  37. }
  38. }

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

  1. PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
  2. 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. Android SDK之API Level

    Android SDK之API Level Android SDK API_LEVEL Platform Version API Level VERSION_CODE Android 4.2, 4.2 ...

  2. iOS-给UIView添加点击事件

    一.当遇到一些UIView 或者 UIView的子类时,比如点击UIImageView要放大图片等. 二.步骤: 1.首先要确保打开控件的用户交互,userInteractionEnabled设置成Y ...

  3. django book 阅读笔记

    一,django是一个十分优秀的python web的框架,那框架的是什么? 假设我们不使用框架来进行编写,我们要用如下的代码进行web脚本: #!/usr/bin/env python import ...

  4. 学期总结ngu

    不知不觉一年就过去了,真可谓光阴似箭,日月如梭,在这一年里,我成长了许多,懂得了如何跟队友合作,提高了我的交际能力,懂得了许多课本知识,增进了我的编写能力.最重要的是学会了总结经验,这无疑是我这一年里 ...

  5. JS 函数--Date()函数

    1.JavaScript没有基本的日期数据类型,所以只能显式的创建Date对象.例如:var myDate=new Date(); 2.为了创建一个存储了特定日期的,或者时间的Date对象,可以简单的 ...

  6. 组合数学 + STL --- 利用STL生成全排列

    Ignatius and the Princess II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ( ...

  7. 数论 - 欧拉函数的运用 --- poj 3090 : Visible Lattice Points

    Visible Lattice Points Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5636   Accepted: ...

  8. P6 EPPM 16 R1 文档和帮助系统

    P6 EPPM 16 R1 文档和帮助系统 https://docs.oracle.com/cd/E74894_01/ http://docs.oracle.com/cd/E68202_01/clie ...

  9. 利用name或id属性设置页面跳转的锚点

    理论准备         网页中的链接按照链接路径的不同,可以分为3种类型,分别是内部类型.锚点链接和外部链接:         按照使用对象的不同,网页中的链接又分为文本超链接,图像超链接,E-ma ...

  10. 2016 一中培训 day 5 ksum

    又是一天的爆零!!!!! 原本第一题 很容易做 竟然优化过度 丢了答案 1693: ksum Time Limit 1000 ms Memory Limit 524288 KBytes Judge S ...