基于opencv 識別、定位二維碼 (c++版)

前言?因工作需要喇潘,需要定位圖片中的二維碼体斩;我遂查閱了相關資料,也學習了opencv開源庫颖低。通過一番努力絮吵,終于很好的實現(xiàn)了二維碼定位。本文將講解如何使用opencv定位二維碼忱屑。

定位二維碼不僅僅是為了識別二維碼蹬敲;還可以通過二維碼對圖像進行水平糾正以及相鄰區(qū)域定位。定位二維碼莺戒,不僅需要圖像處理相關知識伴嗡,還需要分析二維碼的特性,本文先從二維碼的特性講起从铲。

1 二維碼特性

二維碼在設計之初就考慮到了識別問題瘪校,所以二維碼有一些特征是非常明顯的。

二維碼有三個“回“”字形圖案名段,這一點非常明顯阱扬。中間的一個點位于圖案的左上角,如果圖像偏轉伸辟,也可以根據(jù)二維碼來糾正麻惶。

思考題:?為什么是三個點,而不是一個信夫、兩個或四個點窃蹋。

一個點:特征不明顯,不易定位忙迁。不易定位二維碼傾斜角度脐彩。

兩個點:兩個點的次序無法確認,很難確定二維碼是否放正了姊扔。

四個點:無法確定4個點的次序惠奸,從而無法確定二維碼是否放正了。

識別二維碼恰梢,就是識別二維碼的三個點佛南,逐步分析一下這三個點的特性

1 每個點有兩個輪廓梗掰。就是兩個口,大“口”內(nèi)部有一個小“口”嗅回,所以是兩個輪廓及穗。

2 如果把這個“回”放到一個白色的背景下,從左到右绵载,或從上到下畫一條線埂陆。這條線經(jīng)過的圖案黑白比例大約為:黑白比例為1:1:3:1:1。

3 如何找到左上角的頂點娃豹?這個頂點與其他兩個頂點的夾角為90度焚虱。

通過上面幾個步驟,就能識別出二維碼的三個頂點懂版,并且識別出左上角的頂點鹃栽。

2 使用opencv識別二維碼

1) 查找輪廓,篩選出三個二維碼頂點

opencv一個非常重要的函數(shù)就是查找輪廓,就是可以找到一個圖中的縮所有的輪廓躯畴,“回”字形圖案是一個非常的明顯的輪廓民鼓,很容易找到。

1intQrParse::FindQrPoint(Mat& srcImg,vector>& qrPoint)2{3//彩色圖轉灰度圖4Mat src_gray;5cvtColor(srcImg, src_gray, CV_BGR2GRAY);6namedWindow("src_gray");7imshow("src_gray", src_gray);89//二值化10Mat threshold_output;11threshold(src_gray, threshold_output,0,255, THRESH_BINARY | THRESH_OTSU);12Mat threshold_output_copy = threshold_output.clone();13namedWindow("Threshold_output");14imshow("Threshold_output", threshold_output);1516//調(diào)用查找輪廓函數(shù)17vector > contours;18vector hierarchy;19findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0,0));2021//通過黑色定位角作為父輪廓蓬抄,有兩個子輪廓的特點丰嘉,篩選出三個定位角22intparentIdx =-1;23intic =0;2425for(inti =0; i < contours.size(); i++)26{27if(hierarchy[i][2] !=-1&& ic ==0)28{29parentIdx = i;30ic++;31}32elseif(hierarchy[i][2] !=-1)33{34ic++;35}36elseif(hierarchy[i][2] ==-1)37{38ic =0;39parentIdx =-1;40}4143//有兩個子輪廓才是二維碼的頂點44if(ic >=2)45{46boolisQr = QrParse::IsQrPoint(contours[parentIdx], threshold_output_copy);4748//保存找到的三個黑色定位角49if(isQr)50qrPoint.push_back(contours[parentIdx]);5152ic =0;53parentIdx =-1;54}55}5657return0;58}

找到了兩個輪廓的圖元,需要進一步分析是不是二維碼頂點倡鲸,用到如下函數(shù):

boolQrParse::IsQrPoint(vector& contour, Mat& img){//最小大小限定RotatedRect rotatedRect = minAreaRect(contour);if(rotatedRect.size.height <10|| rotatedRect.size.width <10)returnfalse;//將二維碼從整個圖上摳出來cv::Mat cropImg = CropImage(img, rotatedRect);intflag = i++;//橫向黑白比例1:1:3:1:1boolresult = IsQrColorRate(cropImg, flag);returnresult;}

黑白比例判斷函數(shù):

1//橫向和縱向黑白比例判斷2boolQrParse::IsQrColorRate(cv::Mat& image,intflag)3{4boolx = IsQrColorRateX(image, flag);5if(!x)6returnfalse;7booly = IsQrColorRateY(image, flag);8returny;9}10//橫向黑白比例判斷11boolQrParse::IsQrColorRateX(cv::Mat& image,intflag)12{13intnr = image.rows /2;14intnc = image.cols * image.channels();1516vector vValueCount;17vector vColor;18intcount =0;19uchar lastColor =0;2021uchar* data = image.ptr(nr);22for(inti =0; i < nc; i++)23{24vColor.push_back(data[i]);25uchar color = data[i];26if(color >0)27color =255;2829if(i ==0)30{31lastColor = color;32count++;33}34else35{36if(lastColor != color)37{38vValueCount.push_back(count);39count =0;40}41count++;42lastColor = color;43}44}4546if(count !=0)47vValueCount.push_back(count);4849if(vValueCount.size() <5)50returnfalse;5152//橫向黑白比例1:1:3:1:153intindex =-1;54intmaxCount =-1;55for(inti =0; i < vValueCount.size(); i++)56{57if(i ==0)58{59index = i;60maxCount = vValueCount[i];61}62else63{64if(vValueCount[i] > maxCount)65{66index = i;67maxCount = vValueCount[i];68}69}70}7172//左邊 右邊 都有兩個值供嚎,才行73if(index <2)74returnfalse;75if((vValueCount.size() - index) <3)76returnfalse;7778//黑白比例1:1:3:1:179floatrate = ((float)maxCount) /3.00;8081cout<<"flag:"<< flag <<" ";8283floatrate2 = vValueCount[index -2] / rate;84cout<< rate2 <<" ";85if(!IsQrRate(rate2))86returnfalse;8788rate2 = vValueCount[index -1] / rate;89cout<< rate2 <<" ";90if(!IsQrRate(rate2))91returnfalse;9293rate2 = vValueCount[index +1] / rate;94cout<< rate2 <<" ";95if(!IsQrRate(rate2))96returnfalse;9798rate2 = vValueCount[index +2] / rate;99cout<< rate2 <<" ";100if(!IsQrRate(rate2))101returnfalse;102103returntrue;104}105//縱向黑白比例判斷 省略106boolQrParse::IsQrColorRateY(cv::Mat& image,intflag)

boolQrParse::IsQrRate(floatrate){//大概比例 不能太嚴格returnrate >0.6&& rate <1.9;}

2) 確定三個二維碼頂點的次序

通過如下原則確定左上角頂點:二維碼左上角的頂點與其他兩個頂點的夾角為90度黄娘。

1// pointDest存放調(diào)整后的三個點峭状,三個點的順序如下2// pt0----pt13// 4// pt25boolQrParse::AdjustQrPoint(Point* pointSrc, Point* pointDest)6{7boolclockwise;8intindex1[3] = {2,1,0};9intindex2[3] = {0,2,1};10intindex3[3] = {0,1,2};1112for(inti =0; i <3; i++)13{14int*n = index1;15if(i==0)16n = index1;17elseif(i ==1)18n = index2;19else20n = index3;2122double angle = QrParse::Angle(pointSrc[n[0]], pointSrc[n[1]], pointSrc[n[2]], clockwise);23if(angle >80&& angle <99)24{25pointDest[0] = pointSrc[n[2]];26if(clockwise)27{28pointDest[1] = pointSrc[n[0]];29pointDest[2] = pointSrc[n[1]];30}31else32{33pointDest[1] = pointSrc[n[1]];34pointDest[2] = pointSrc[n[0]];35}36returntrue;37}38}39returntrue;40}

3)通過二維碼對圖片矯正。

圖片有可能是傾斜的逼争,傾斜夾角可以通過pt0與pt1連線與水平線之間的夾角確定优床。二維碼的傾斜角度就是整個圖片的傾斜角度,從而可以對整個圖片進行水平矯正誓焦。

1//二維碼傾斜角度2Point hor(pointAdjust[0].x+300,pointAdjust[0].y);//水平線3doubleqrAngle =QrParse::Angle(pointAdjust[1], hor, pointAdjust[0], clockwise);45//以二維碼左上角點為中心 旋轉6Mat drawingRotation =Mat::zeros(Size(src.cols,src.rows), CV_8UC3);7doublerotationAngle = clockwise? -qrAngle:qrAngle;8Mat affine_matrix = getRotationMatrix2D(pointAdjust[0], rotationAngle,1.0);//求得旋轉矩陣9warpAffine(src, drawingRotation, affine_matrix, drawingRotation.size());

4)二維碼相鄰區(qū)域定位

一般情況下胆敞,二維碼在整個圖中的位置是確定的。識別出二維碼后杂伟,根據(jù)二維碼與其他圖的位置關系移层,可以很容易的定位別的圖元。

后記

作者通過查找大量資料赫粥,仔細研究了二維碼的特征观话,從而找到了識別二維碼的方法。網(wǎng)上也有許多識別二維碼的方法越平,但是不夠嚴謹频蛔。本文是將二維碼的多個特征相結合來識別灵迫,這樣更準確。這種識別方法已應用在公司的產(chǎn)品中晦溪,識別效果還是非常好的瀑粥。

看我主頁簡介免費C++學習資源,視頻教程三圆、職業(yè)規(guī)劃狞换、面試詳解、學習路線舟肉、開發(fā)工具

每晚8點直播講解C++編程技術哀澈。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市度气,隨后出現(xiàn)的幾起案子割按,更是在濱河造成了極大的恐慌,老刑警劉巖磷籍,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件适荣,死亡現(xiàn)場離奇詭異,居然都是意外死亡院领,警方通過查閱死者的電腦和手機弛矛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來比然,“玉大人丈氓,你說我怎么就攤上這事∏糠ǎ” “怎么了万俗?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饮怯。 經(jīng)常有香客問我闰歪,道長,這世上最難降的妖魔是什么蓖墅? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任库倘,我火速辦了婚禮,結果婚禮上论矾,老公的妹妹穿的比我還像新娘教翩。我一直安慰自己,他們只是感情好贪壳,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布饱亿。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪路捧。 梳的紋絲不亂的頭發(fā)上关霸,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音杰扫,去河邊找鬼队寇。 笑死,一個胖子當著我的面吹牛章姓,可吹牛的內(nèi)容都是我干的佳遣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼凡伊,長吁一口氣:“原來是場噩夢啊……” “哼零渐!你這毒婦竟也來了?” 一聲冷哼從身側響起系忙,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤诵盼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后银还,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體风宁,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年蛹疯,在試婚紗的時候發(fā)現(xiàn)自己被綠了戒财。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡捺弦,死狀恐怖饮寞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情列吼,我是刑警寧澤幽崩,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站冈欢,受9級特大地震影響歉铝,放射性物質發(fā)生泄漏。R本人自食惡果不足惜凑耻,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柠贤。 院中可真熱鬧香浩,春花似錦、人聲如沸臼勉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宴霸。三九已至囱晴,卻和暖如春膏蚓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畸写。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工驮瞧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枯芬。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓论笔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親千所。 傳聞我的和親對象是個殘疾皇子狂魔,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345