安卓等间采样缩放算法的实现与解决ZXing生成DATA MATRIX二维码太小的问题
2017-08-11 20:28
399 查看
本文相关代码:https://gitee.com/mingyueyixi/ZxingLibraryTest
按我的上一篇文章所述,修改生成二维码的方法后,成功生成了DATA MATRIX格式的二维码,然而,这个二维码实在太小,以至于竟然看不见,堪比小芝麻。而Bitmap.createScaleBitmap()方法放大的图片又十分模糊,无法使用。
于是,寻找了等间采样算法来进行缩放。关于这个算法缩放的图片,其原理、优缺点自行研究,闲言少叙,以下是Android平台下实现等间采样缩放算法的代码:
BitmapFlex.java
接着,用它来缩放data matrix、pdf417格式的二维码。pdf417格式的二维码好生奇葩,好多时候是长方形的。
注:
RSS_EXPANDED
RSS_14
BarcodeFormat
UPC_EAN_EXTENSION
UPCE_E
以及CODE_93
由于某些原因生成失败,暂未排查完毕。(其中有部分是扩展的条形码,可能需要自己写相关代码吧。)
上诉修改过后的代码,支持正常生成ZXing的所有二维码格式。
最后,利用这个工具类生成二维码。
图片像素很大,可以在新建标签中打开查看。
本文地址:
我的csdn博客地址:
额,混入了一个一维码,条形码虽然放大了那么多倍,但是估计也没人扫的出,太密集了。
按我的上一篇文章所述,修改生成二维码的方法后,成功生成了DATA MATRIX格式的二维码,然而,这个二维码实在太小,以至于竟然看不见,堪比小芝麻。而Bitmap.createScaleBitmap()方法放大的图片又十分模糊,无法使用。
于是,寻找了等间采样算法来进行缩放。关于这个算法缩放的图片,其原理、优缺点自行研究,闲言少叙,以下是Android平台下实现等间采样缩放算法的代码:
BitmapFlex.java
import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.util.Log; /** * @author Yue * @date 2017/8/9 13:28 * 此类中的方法已通过测试。主要用于等间采样缩放。等间采样缩放在单一色彩的图中表现较好,不会产生模糊, * 可用于放大 DATA_MATRIX 二维码。 */ public class BitmapFlex { /** * 等间隔采样的图像缩放 * @param bitmap 要缩放的图像对象 * @param dstWidth 缩放后图像的宽 * @param dstHeight 缩放后图像的高 * @return 返回处理后的图像对象 */ public static Bitmap flex(Bitmap bitmap, int dstWidth, int dstHeight) { float wScale = (float) dstWidth / bitmap.getWidth(); float hScale = (float) dstHeight / bitmap.getHeight(); return flex(bitmap, wScale, hScale); } /** * 等间隔采样的图像缩放 * @param bitmap 要缩放的bitap对象 * @param wScale 要缩放的横列(宽)比列 * @param hScale 要缩放的纵行(高)比列 * @return 返回处理后的图像对象 */ public static Bitmap flex(Bitmap bitmap, float wScale, float hScale) { if (wScale <= 0 || hScale <= 0){ return null; } float ii = 1 / wScale; //采样的行间距 float jj = 1 / hScale; //采样的列间距 int width = bitmap.getWidth(); int height = bitmap.getHeight(); int dstWidth = (int) (wScale * width); int dstHeight = (int) (hScale * height); int[] pixels = new int[width * height]; bitmap.getPixels(pixels, 0, width, 0, 0, width, height); int[] dstPixels = new int[dstWidth * dstHeight]; for (int j = 0; j < dstHeight; j++) { for (int i = 0; i < dstWidth; i++) { dstPixels[j * dstWidth + i] = pixels[(int) (jj * j) * width + (int) (ii * i)]; } } System.out.println((int) ((dstWidth - 1) * ii)); Log.d(">>>",""+"dstPixels:"+dstWidth+" x "+dstHeight); Bitmap outBitmap = Bitmap.createBitmap(dstWidth, dstHeight, Config.ARGB_8888); outBitmap.setPixels(dstPixels, 0, dstWidth, 0, 0, dstWidth, dstHeight); return outBitmap; } }
接着,用它来缩放data matrix、pdf417格式的二维码。pdf417格式的二维码好生奇葩,好多时候是长方形的。
package banding.com.google.Zxing.utils; import android.graphics.Bitmap; import android.graphics.Canvas; import android.text.TextUtils; import android.util.Log; import com.google.zxing.BarcodeFormat; import com.google.zxing.Binarizer; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.EncodeHintType; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.NotFoundException; import com.google.zxing.RGBLuminanceSource; import com.google.zxing.Result; import com.google.zxing.WriterException; import com.google.zxing.aztec.encoder.Encoder; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import java.util.HashMap; import java.util.Map; /** * 条码码操作类。生成时需要特别注意,条码格式对字符编码有要求,有的不支持中文、字母等,需要依据格式额外处理。 * 注意,配置: * <p> {@link BarcodeFormat#RSS_EXPANDED,BarcodeFormat#RSS_14,BarcodeFormat#UPC_EAN_EXTENSION} * * 以及code 93 由于某些原因生成失败,暂未排查完毕。 * */ public class BarcodeUtils { /** * 默认前景色黑色,背景色透明 * 生成一二维码 * @param content 二维码文字内容 * @param width 图片像素宽 * @param height 图片像素高 * @param format 二维码图片的编码方式 * @return 二维码bitmap */ public static Bitmap createBarcode(String content, int width, int height, BarcodeFormat format) { return createBarcode(content, width, height, format, 0xff000000, 0x00000000); } /** * 生成二维码(部分一维码不能使用此方法) * @param content 二维码文字内容 * @param width 图片像素宽 * @param height 图片像素高 * @param format 二维码图片的编码方式 * @param foreColor 前景色 * @param backColor 背景色 * @return Bitmap对象 */ public static Bitmap createBarcode(String content, int width, int height, BarcodeFormat format, int foreColor, int backColor) { return createBarcode(content, "utf-8", width, height, format, foreColor, backColor); } /** * 生成一二维码 * @param content 二维码文字内容 * @param charset 字符编码 * @param width 图片像素宽 * @param height 图片像素高 * @param format 二维码图片的编码方式 * @param foreColor 前景色 * @param backColor 背景色 * @return Bitmap对象 */ public static Bitmap createBarcode(String content, String charset, int width, int height, BarcodeFormat format, int foreColor, int backColor) { if (TextUtils.isEmpty(content)) { return null; } if (TextUtils.isEmpty(charset)){ charset = "utf-8"; } //配置参数 HashMap<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(); hints.put(EncodeHintType.CHARACTER_SET, charset);//字符串编码 //错误纠正,Aztec格式,pdf417格式和其它条码配置不同,不能通用,否则会产生错误。 if (format == BarcodeFormat.AZTEC) {//错误校正词的最小百分比 hints.put(EncodeHintType.ERROR_CORRECTION, Encoder.DEFAULT_EC_PERCENT);//默认,可以不设 } else if (format == BarcodeFormat.PDF_417) { hints.put(EncodeHintType.ERROR_CORRECTION, 2);//纠错级别,允许为0到8。默认2,可以不设 } else { hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);// } //设置空白边距的宽度,默认值为4 hints.put(EncodeHintType.MARGIN, 0); return createBarcode(content, width, height, format, hints, foreColor, backColor); } /** * 生成一二维码。 * @param content 文字内容 * @param format 条码编码方式 * @param hints 条码其他配置对象 * @param foreColor 条码前景色 * @param backColor 条码背景色 * @return bitmap 条码bitmap对象 */ public static Bitmap createBarcode(String content, int width, int height, BarcodeFormat format, HashMap<EncodeHintType, Object> hints, int foreColor, int backColor) { return createBarcode(content, width, height, format, hints, foreColor, backColor, true); } /** * QR二维码生成 * * @param content * @param widthAndHeight * @return 二维码bitmap */ public static Bitmap createQRCode(String content, int widthAndHeight) { return createBarcode(content, widthAndHeight, widthAndHeight, BarcodeFormat.QR_CODE); } /** * 128条形码 * * @param content * @param width * @param height * @return 一维码bitmap */ public static Bitmap create128Code(String content, int width, int height) { return createBarcode(content, width, height, BarcodeFormat.CODE_128); } /** * 条形码生成方法 * * @param content 文字内容 * @param width 宽 * @param height 高 * @param format 条形码格式,pdf417,code128,QRcode等 * @param hints 条码的Map配置 * @param foreColor 前景色 * @param backColor 背景色 * @param fitSizeFlag 是否自动适应到预期的宽高(data matrix格式的二维码,生成时非常小,需要放大,其他格式也可能不到预期的大小) * <p> true,使用等间采样算法,缩放bitmap到期望的尺寸 * <p> false则不处理 * @return 条码 */ public static Bitmap createBarcode(String content, int width, int height, BarcodeFormat format, Map<EncodeHintType, Object> hints, int foreColor, int backColor, boolean fitSizeFlag) { // 图像数据转换,使用了矩阵转换 BitMatrix bitMatrix = null; try { MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); bitMatrix = multiFormatWriter.encode(content, format, width, height, hints); } catch (WriterException e) { e.printStackTrace(); } if (bitMatrix == null) { return null; } //注,以下代码修正DATA_MATRIX和PDF_417生成错误。 //网络上广泛使用预设的width和height生成像素矩阵,因这两种格式的二维码,其bitMatrix不一定等于预期的像素矩阵(width * height) //会导致数组下标异常 int bitWidth = bitMatrix.getWidth(); int bitHeight = bitMatrix.getHeight(); int[] pixels = new int[bitWidth * bitHeight]; //遍历bitmatrix,为像素矩阵按一行行(横列)设置像素颜色。 for (int y = 0; y < bitHeight; y++) { for (int x = 0; x < bitWidth; x++) { if (bitMatrix.get(x, y)) { pixels[y * bitWidth + x] = foreColor; } else { pixels[y * bitWidth + x] = backColor; } } } Bitmap bitmap = Bitmap.createBitmap(bitWidth, bitHeight, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, bitWidth, 0, 0, bitWidth, bitHeight); Log.d(">>>", "预期宽高:" + width + " * " + height + " 矩阵宽高:" + bitWidth + " * " + bitHeight); if (fitSizeFlag) { //因为bitmap可能并不等于预先设置的width和height,需要进行等比缩放,尤其是BarcodeFormat.DATA_MATRIX格式,小的不可想象 float wMultiple = ((float) bitWidth) / (float) width;//生成的bitmap的宽除以预期的宽 float hMultiple = ((float) bitHeight) / (float) height;//生成的bitmap的高除以预期的高 Log.d(">>>", wMultiple + "--" + hMultiple); if (wMultiple == 1f || hMultiple == 1f) {//说明生成的条形码符合预期,不需要缩放 Log.d(">>>", "...re1"); return bitmap; } if (wMultiple > hMultiple) {//说明宽超出范围更多,以宽的比例为标准进行缩放。 int dstWidth = width;// bitWidth / wMultiple int dstHeight = (int) (bitHeight / wMultiple); // bitmap = Bitmap.createScaledBitmap(bitmap,dstWidth,dstHeight,true);//安卓的这个方法不行 bitmap = BitmapFlex.flex(bitmap, dstWidth, dstHeight);//等间采样算法进行缩放 } else {//说明相当或高超出范围更多,以高的比例为标准进行缩放。 int dstHeight = height;// bitHeight / hMultiple int dstWidth = (int) (bitWidth / hMultiple); bitmap = BitmapFlex.flex(bitmap, dstWidth, dstHeight);//等间采样算法进行缩放 } } return bitmap; } /** * 一、二维码的解析 * @param bitmap 图片对象 * @param charaterset 编码。以此文字编码解析一二维码文字内容 * @return 解析结果 */ public static Result parseCode(Bitmap bitmap, String charaterset) { Map<DecodeHintType, String> hints = new HashMap<DecodeHintType, String>(); hints.put(DecodeHintType.CHARACTER_SET, charaterset); return parseBarCode(bitmap,hints); } /** * 一二维码的解析 * @param bitmap 图片对象 * @param hints 解码map配置 * @return 解析结果 */ public static Result parseBarCode(Bitmap bitmap,Map<DecodeHintType,?> hints){ MultiFormatReader formatReader = new MultiFormatReader(); int bw = bitmap.getWidth(); int bh = bitmap.getHeight(); int[] pixels = new int[bw * bh]; bitmap.getPixels(pixels, 0, bw, 0, 0, bw, bh); LuminanceSource source = new RGBLuminanceSource(bw, bh, pixels); Binarizer binarizer = new HybridBinarizer(source); BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); Result result = null; try { result = formatReader.decode(binaryBitmap, hints); } catch (NotFoundException e) { e.printStackTrace(); } return result; } /** * 在bitma中间添加Logo图案(用于QR二维码等场合) * @param src 原图 * @param logo logo图 */ public static void drawLogo(Bitmap src, Bitmap logo) { if (src == null) { return; } if (logo == null) { return; } //获取图片的宽高 int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int logoWidth = logo.getWidth(); int logoHeight = logo.getHeight(); if (srcWidth == 0 || srcHeight == 0) { return; } if (logoWidth == 0 || logoHeight == 0) { return; } //logo大小为二维码整体大小的1/5 float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; // Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); try { Canvas canvas = new Canvas(src); canvas.drawBitmap(src, 0, 0, null); canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); } catch (Exception e) { e.getStackTrace(); } } }
注:
RSS_EXPANDED
RSS_14
BarcodeFormat
UPC_EAN_EXTENSION
UPCE_E
以及CODE_93
由于某些原因生成失败,暂未排查完毕。(其中有部分是扩展的条形码,可能需要自己写相关代码吧。)
上诉修改过后的代码,支持正常生成ZXing的所有二维码格式。
最后,利用这个工具类生成二维码。
图片像素很大,可以在新建标签中打开查看。
本文地址:
我的csdn博客地址:
额,混入了一个一维码,条形码虽然放大了那么多倍,但是估计也没人扫的出,太密集了。
相关文章推荐
- 生成、读取 二维码(QR码)-采用ZXing(已解决中文乱码的问题)
- 解决ZXING生成二维码图片白框太大的问题
- 解决ZXING生成二维码图片白框太大的问题
- zxing开源项目 解决生成二维码保存的问题
- 解决低版本 ZXING生成二维码图片白框太大的问题
- 安卓Zxing生成Data Matrix、PDF417二维码错误:数组下标异常
- Zxing实现在线二维码生成程序
- Zxing实现在线二维码生成程序
- 安卓开发环境的搭建和解决在Eclipse新建安卓5.1工程不能自动生成R文件的问题
- Android使用Zxing扫描二维码过程解决三星note1竖屏后花屏问题
- zxing实现二维码生成和解析
- 安卓生成二维码 ==。以及中文乱码问题
- 解决jdk1.4生成二维码问题,因为现下生成二维码的jar包,版本最低支持jdk1.5以上的版本,老的项目中想使用二维码,直接使用jar包无法使用
- android 中使用Zxing实现二维码的解码和二维码的生成
- Zxing 实现二维码的生成方法
- C#利用zxing.net生成二维码和条形码并实现打印的功能
- zxing实现二维码生成和解析
- zxing实现二维码生成和解析
- 随机字符串解决大问题之腾讯网如何实现手机扫描二维码登录qq功能的