二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按必定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,经过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具备条码技术的一些共性:每种码制有其特定的字符集;每一个字符占有必定的宽度;具备必定的校验功能等。同时还具备对不一样行的信息自动识别功能、及处理图形旋转变化点。java
定位图案android
经过查找定位图案,能够实现二维码扫描的检测和定位。git
先对图片进行灰度处理:github
image = image.getImage().convert2Gray().getProcessor();
ByteProcessor src = ((ByteProcessor)image);复制代码
再对图像作二值化处理:算法
Threshold t = new Threshold();
t.process(src, Threshold.THRESH_OTSU, Threshold.METHOD_THRESH_BINARY_INV, 20);复制代码
而后是对y、x方向进行形态学上的开操做canvas
MorphOpen mOpen = new MorphOpen();
byte[] data = new byte[width*height];
System.arraycopy(src.getGray(), 0, data, 0, data.length);
ByteProcessor copy = new ByteProcessor(data, width, height);
mOpen.process(src, new Size(n1, n2)); // Y方向开操做
src.getImage().resetBitmap();
mOpen.process(copy, new Size(n2, n1)); // X方向开操做
CV4JImage cv4JImage = new CV4JImage(width,height);
((ByteProcessor)cv4JImage.getProcessor()).putGray(copy.getGray());复制代码
所谓开操做是指先腐蚀后膨胀的操做。在以前的文章二值图像分析:案例实战(文本分离+硬币计数)曾经介绍过开操做的用途。小程序
import com.cv4j.core.datamodel.ByteProcessor;
import com.cv4j.core.datamodel.Size;
public class MorphOpen {
/** * in order to remove litter noise block, erode + dilate operator * * @param binary * @param structureElement */
public void process(ByteProcessor binary, Size structureElement) {
FastErode erode = new FastErode();
FastDilate dilate = new FastDilate();
erode.process(binary, structureElement, 1);
dilate.process(binary, structureElement, 1);
}
}复制代码
接下来是标记联通区域,找到二维码的三个特征区域,也就是定位图案。微信
// 联通组件查找链接区域
ConnectedAreaLabel ccal = new ConnectedAreaLabel();
ccal.setFilterNoise(true);
List<Rect> rectList = new ArrayList<>();
int[] labelMask = new int[width*height];
ccal.process(src, labelMask, rectList, true);
float w = 0;
float h = 0;
float rate = 0;
List<Rect> qrRects = new ArrayList<>();
for(Rect roi : rectList) {
if (roi == null) continue;
if((roi.width > width/4 || roi .width < 10) || (roi.height < 10 || roi.height > height/4))
continue;
if((roi.x < 10 || roi.x > width -10)|| (roi.y < 10 || roi.y > height-10))
continue;
w = roi.width;
h = roi.height;
rate = (float)Math.abs(w / h - 1.0);
if(rate < 0.05 && isRect(roi, labelMask, width, height,true)) {
qrRects.add(roi);
}
}复制代码
最后,经过定位图案可以找到二维码所在的区域,若是找不到会返回空的矩形。不然返回一个Rect,它表示找到的二维码所在图像中的区域。post
咱们能够对该区域进行标识,下面是算法的具体使用,找到图像中的二维码以后,用红色的边框框起来。优化
CV4JImage cv4JImage = new CV4JImage(bitmap);
QRCodeScanner qrCodeScanner = new QRCodeScanner();
Rect rect = qrCodeScanner.findQRCodeBounding(cv4JImage.getProcessor(),1,6);
Bitmap bm = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(bm);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth((float) 10.0);
paint.setStyle(Paint.Style.STROKE);
android.graphics.Rect androidRect = new android.graphics.Rect(rect.x-20,rect.y-20,rect.br().x+20,rect.br().y+20);
canvas.drawRect(androidRect,paint);
image.setImageBitmap(bm);复制代码
对于iPhone截屏以后的图片,该图片尺寸是1242 × 2208。在没有对图片作任何缩放处理的状况下,使用该算法进行定位二维码的区域也是ok的。
固然,对于大图若是适当地降采样处理或者缩放的话,算法速度会更快。
彩色二维码和小程序的圆形二维码目前可以检测吗?
暂时不能。由于图像在二值化以后,彩色的部分像素点会变成白色的像素点,致使二维码轮廓不完整,最终致使没法实现二值分析。咱们会在完成模版匹配的功能以后,继续优化算法完善该功能,加上检测彩色和圆形二维码的能力。
算法的源码位于cv4j的QRCodeScanner中,该算法不能识别二维码的字符串,只能找到二维码的区域,若是须要识别二维码仍是须要使用Google Zxing。
cv4j 是gloomyfish和我一块儿开发的图像处理库,纯java实现,目前还处于早期的版本。
文章中的算法是对二值图像分析的综合运用,使用它再结合Google的ZXing可以提升二维码的识别率。固然,因为它是pure java实现的,稍做改动可以用它来判断出某张图片中是否包含有二维码。
若是您想看该系列先前的文章能够访问下面的文集:
www.jianshu.com/nb/10401400