二維條碼/二維碼(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