二維碼
二維條碼/二維碼(2-dimensional bar code)是用某種特定的幾何圖形按一定規(guī)律在平面(二維方向上)分布的黑白相間的圖形記錄數(shù)據(jù)符號(hào)信息的孩饼;在代碼編制上巧妙地利用構(gòu)成計(jì)算機(jī)內(nèi)部邏輯基礎(chǔ)的“0”、“1”比特流的概念竹挡,使用若干個(gè)與二進(jìn)制相對(duì)應(yīng)的幾何形體來(lái)表示文字?jǐn)?shù)值信息镀娶,通過(guò)圖象輸入設(shè)備或光電掃描設(shè)備自動(dòng)識(shí)讀以實(shí)現(xiàn)信息自動(dòng)處理:它具有條碼技術(shù)的一些共性:每種碼制有其特定的字符集;每個(gè)字符占有一定的寬度揪罕;具有一定的校驗(yàn)功能等梯码。同時(shí)還具有對(duì)不同行的信息自動(dòng)識(shí)別功能、及處理圖形旋轉(zhuǎn)變化點(diǎn)好啰。
定位圖案
- Position Detection Pattern是定位圖案轩娶,用于標(biāo)記二維碼的矩形大小冒晰。這三個(gè)定位圖案有白邊叫Separators for Postion Detection Patterns庆捺。之所以三個(gè)而不是四個(gè)意思就是三個(gè)就可以標(biāo)識(shí)一個(gè)矩形了寄狼。
- Timing Patterns也是用于定位的厚宰。原因是二維碼有40種尺寸,尺寸過(guò)大了后需要有根標(biāo)準(zhǔn)線涌庭,不然掃描的時(shí)候可能會(huì)掃歪了嚼隘。
- Alignment Patterns 只有Version 2以上(包括Version2)的二維碼需要這個(gè)東東保屯,同樣是為了定位用的秉版。
通過(guò)查找定位圖案贤重,可以實(shí)現(xiàn)二維碼掃描的檢測(cè)和定位。
檢測(cè)和定位的步驟
先對(duì)圖片進(jìn)行灰度處理:
image = image.getImage().convert2Gray().getProcessor();
ByteProcessor src = ((ByteProcessor)image);
再對(duì)圖像做二值化處理:
Threshold t = new Threshold();
t.process(src, Threshold.THRESH_OTSU, Threshold.METHOD_THRESH_BINARY_INV, 20);
然后是對(duì)y清焕、x方向進(jìn)行形態(tài)學(xué)上的開(kāi)操作
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方向開(kāi)操作
src.getImage().resetBitmap();
mOpen.process(copy, new Size(n2, n1)); // X方向開(kāi)操作
CV4JImage cv4JImage = new CV4JImage(width,height);
((ByteProcessor)cv4JImage.getProcessor()).putGray(copy.getGray());
所謂開(kāi)操作是指先腐蝕后膨脹的操作并蝗。在之前的文章二值圖像分析:案例實(shí)戰(zhàn)(文本分離+硬幣計(jì)數(shù))曾經(jīng)介紹過(guò)開(kāi)操作的用途。
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);
}
}
接下來(lái)是標(biāo)記聯(lián)通區(qū)域秸妥,找到二維碼的三個(gè)特征區(qū)域滚停,也就是定位圖案。
// 聯(lián)通組件查找連接區(qū)域
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);
}
}
最后粥惧,通過(guò)定位圖案能夠找到二維碼所在的區(qū)域键畴,如果找不到會(huì)返回空的矩形。否則返回一個(gè)Rect影晓,它表示找到的二維碼所在圖像中的區(qū)域。
我們可以對(duì)該區(qū)域進(jìn)行標(biāo)識(shí)檩禾,下面是算法的具體使用挂签,找到圖像中的二維碼之后,用紅色的邊框框起來(lái)盼产。
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);
對(duì)于iPhone截屏之后的圖片饵婆,該圖片尺寸是1242?×?2208。在沒(méi)有對(duì)圖片做任何縮放處理的情況下戏售,使用該算法進(jìn)行定位二維碼的區(qū)域也是ok的侨核。
當(dāng)然草穆,對(duì)于大圖如果適當(dāng)?shù)亟挡蓸犹幚砘蛘呖s放的話,算法速度會(huì)更快搓译。
寫(xiě)在最后
彩色二維碼和小程序的圓形二維碼目前能夠檢測(cè)嗎悲柱?
暫時(shí)不能。因?yàn)閳D像在二值化之后些己,彩色的部分像素點(diǎn)會(huì)變成白色的像素點(diǎn)豌鸡,導(dǎo)致二維碼輪廓不完整,最終導(dǎo)致無(wú)法實(shí)現(xiàn)二值分析段标。我們會(huì)在完成模版匹配的功能之后涯冠,繼續(xù)優(yōu)化算法完善該功能,加上檢測(cè)彩色和圓形二維碼的能力逼庞。
算法的源碼位于cv4j的QRCodeScanner中蛇更,該算法不能識(shí)別二維碼的字符串,只能找到二維碼的區(qū)域赛糟,如果需要識(shí)別二維碼還是需要使用Google Zxing派任。
總結(jié)
cv4j 是gloomyfish和我一起開(kāi)發(fā)的圖像處理庫(kù),純java實(shí)現(xiàn)虑灰,目前還處于早期的版本吨瞎。
文章中的算法是對(duì)二值圖像分析的綜合運(yùn)用,使用它再結(jié)合Google的ZXing能夠提高二維碼的識(shí)別率穆咐。當(dāng)然颤诀,由于它是pure java實(shí)現(xiàn)的,稍作改動(dòng)能夠用它來(lái)判斷出某張圖片中是否包含有二維碼对湃。
如果您想看該系列先前的文章可以訪問(wèn)下面的文集:
http://www.reibang.com/nb/10401400