安卓等间采样缩放算法的实现与解决ZXing生成DATA MATRIX二维码太小的问题

来源:互联网 发布:有趣的javascript例子 编辑:程序博客网 时间:2024/05/21 18:47

本文相关代码:https://gitee.com/mingyueyixi/ZxingLibraryTest

按我的上一篇文章所述,修改生成二维码的方法后,成功生成了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博客地址:

这里写图片描述]![这里写图片描述

额,混入了一个一维码,条形码虽然放大了那么多倍,但是估计也没人扫的出,太密集了。

原创粉丝点击