條形碼是當前超市和部分工廠使用比較廣泛的物品,產品標識技術,使用攝像頭檢測一張圖片的條形碼包含有兩個步驟,第一是定位條形碼的位置,定位以後剪切出條形碼,而且識別出條形碼對應的字符串,而後就能夠調用網絡,數據庫等手段快速進行後續處理.ios
條形碼識別要考慮到條形碼的特色,本文針對的是條形碼在圖片中的位置相對垂直,沒有各類傾斜的那種條形碼,以下圖所示算法
要定位首先要檢視這種條形碼的特色,這種圖像在X方向上的梯度確定很明顯,同時,Y方向的梯度就沒這麼明顯,因此第一步,咱們應該將圖像的灰度圖像分別計算梯度,用X方向梯度減去Y方向梯度,這樣能夠保留X方向特徵而且去除Y方向的干擾,處理以後圖像以下所示數據庫
能夠看到,二維碼對一維碼的定位造成了干擾,可是二維碼的空間漏洞相對一維碼多很懂,因而咱們考慮進行一次模糊而且二值化,看能不能有所效果,以下(記得調整相應的模糊化參數和閾值參數,獲得相對最好的結果)網絡
有必定的效果,可是此時又出現問題條形碼出現了黑色的縫隙,不利於定位完整區域,這個時候要進行一些形態學操做,去除黑色縫隙,咱們選擇閉運算,算子根據縫隙的狀況,寬度大於高度,矩形縫隙.處理之後的結果.spa
效果能夠,又出現問題,二維碼的區域連着,仍是面積很大,對後面咱們算區域面積依然有影響,可是咱們觀測二維碼的鏈接區域明顯要比一維碼的鏈接區域要細不少,也就是說,咱們能夠很快的腐蝕斷二維碼的鏈接,同時還保持一維碼的鏈接,而後在膨脹回來,二維碼的鏈接斷開就應該不會有這個大塊的區域連着了,注意,膨脹和腐蝕的次數應當是一致的,保證獲得結果區域的準確.我選擇膨脹腐蝕四次,先膨脹斷開二維碼鏈接,最後的結果顯示以下.net
此時,二維碼的影響就基本沒有了,如今咱們只須要先查找輪廓,而後計算圖像中每一個輪廓的面積,選出面積最大的那個輪廓,計算這個輪廓的最小外包矩形,就能找到相應的圖像區域了.這樣操做的結果和切分出來的條形碼以下所示code
到目前爲止,咱們已經完成了條形碼的位置定位,而且剪切出了條形碼的團,接下里對這個圖案進行識別,識別以前,總結一下xml
接下來條形碼識別,可使用zbar識別庫,庫的簡介就不說了,能夠本身去官網下載,安裝時候記得選上第三個選項,不然沒有頭文件.blog
安裝完成後,到安裝目錄,將bin目錄加入環境變量,在VS中VC++目錄的include中加入頭文件地址,lib地址,並加入lib名稱(鏈接器-輸入-附加依賴項),而後就可使用了,具體使用查看下面的代碼,結果以下圖片
代碼以下
#include <opencv2/opencv.hpp> #include <iostream> #include <zbar.h> using namespace cv; using namespace std; using namespace zbar; int main(int argc,char* argv[]) { char fileNameString[100]; char windowNameString[50]; char resultFileNameSring[100]; Mat srcImage,grayImage,blurImage,thresholdImage,gradientXImage,gradientYImage,gradientImage,morphImage; for (int fileCount = 1;fileCount < 8;fileCount++) { sprintf(fileNameString,"F:\\opencv\\條形碼檢測與識別\\barcode_0%d.jpg",fileCount); sprintf(windowNameString,"result 0%d",fileCount); sprintf(resultFileNameSring,"F:\\opencv\\條形碼檢測與識別\\barcodeResult_0%d.jpg",fileCount); //讀取圖像 srcImage = imread(fileNameString); if(srcImage.empty()) { cout<<"image file read error"<<endl; return -1; } //圖像轉換爲灰度圖像 if(srcImage.channels() == 3) { cvtColor(srcImage,grayImage,CV_RGB2GRAY); } else { grayImage = srcImage.clone(); } //創建圖像的梯度幅值 Scharr(grayImage,gradientXImage,CV_32F,1,0); Scharr(grayImage,gradientYImage,CV_32F,0,1); //由於咱們須要的條形碼在須要X方向水平,因此更多的關注X方向的梯度幅值,而省略掉Y方向的梯度幅值 subtract(gradientXImage,gradientYImage,gradientImage); //歸一化爲八位圖像 convertScaleAbs(gradientImage,gradientImage); //看看獲得的梯度圖像是什麼樣子 //imshow(windowNameString,gradientImage); //對圖片進行相應的模糊化,使一些噪點消除 blur(gradientImage,blurImage,Size(9,9)); //模糊化之後進行閾值化,獲得到對應的黑白二值化圖像,二值化的閾值能夠根據實際狀況調整 threshold(blurImage,thresholdImage,210,255,THRESH_BINARY); //看看二值化圖像 //imshow(windowNameString,thresholdImage); //二值化之後的圖像,條形碼之間的黑白沒有鏈接起來,就要進行形態學運算,消除縫隙,至關於小型的黑洞,選擇閉運算 //由於是長條之間的縫隙,因此須要選擇寬度大於長度 Mat kernel = getStructuringElement(MORPH_RECT,Size(21,7)); morphologyEx(thresholdImage,morphImage,MORPH_CLOSE,kernel); //看看形態學操做之後的圖像 //imshow(windowNameString,morphImage); //如今要讓條形碼區域鏈接在一塊兒,因此選擇膨脹腐蝕,並且爲了保持圖形大小基本不變,應該使用相同次數的膨脹腐蝕 //先腐蝕,讓其餘區域的亮的地方變少最好是消除,而後膨脹回來,消除干擾,迭代次數根據實際狀況選擇 erode(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4); dilate(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4); //看看形態學操做之後的圖像 //imshow(windowNameString,morphImage); vector<vector<Point2i>>contours; vector<float>contourArea; //接下來對目標輪廓進行查找,目標是爲了計算圖像面積 findContours(morphImage,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE); //計算輪廓的面積而且存放 for(int i = 0; i < contours.size();i++) { contourArea.push_back(cv::contourArea(contours[i])); } //找出面積最大的輪廓 double maxValue;Point maxLoc; minMaxLoc(contourArea, NULL,&maxValue,NULL,&maxLoc); //計算面積最大的輪廓的最小的外包矩形 RotatedRect minRect = minAreaRect(contours[maxLoc.x]); //爲了防止找錯,要檢查這個矩形的偏斜角度不能超標 //若是超標,那就是沒找到 if(minRect.angle<2.0) { //找到了矩形的角度,可是這是一個旋轉矩形,因此還要從新得到一個外包最小矩形 Rect myRect = boundingRect(contours[maxLoc.x]); //把這個矩形在源圖像中畫出來 //rectangle(srcImage,myRect,Scalar(0,255,255),3,LINE_AA); //看看顯示效果,找的對不對 //imshow(windowNameString,srcImage); //將掃描的圖像裁剪下來,並保存爲相應的結果,保留一些X方向的邊界,因此對rect進行必定的擴張 myRect.x= myRect.x - (myRect.width/20); myRect.width = myRect.width*1.1; Mat resultImage = Mat(srcImage,myRect); if(!imwrite(resultFileNameSring,resultImage)) { cout<<"file save error!"<<endl; return -2; } } } //檢測到了以後進行條形碼識別 FileStorage file("F:\\opencv\\條形碼檢測與識別\\result.xml",FileStorage::WRITE); for (int fileCount = 1;fileCount < 8;fileCount++) { sprintf(resultFileNameSring,"F:\\opencv\\條形碼檢測與識別\\barcodeResult_0%d.jpg",fileCount); sprintf(windowNameString,"result 0%d",fileCount); Mat result = imread(resultFileNameSring); if(!result.empty()) { //如今開始識別 cvtColor(result,grayImage,CV_RGB2GRAY); int width = grayImage.cols; // extract dimensions int height = grayImage.rows; Image image(width,height,"Y800",grayImage.data,width*height); ImageScanner scanner; scanner.set_config(ZBAR_NONE,ZBAR_CFG_ENABLE,1); int n = scanner.scan(image); for (Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end();++symbol) { cout <<"pic name:\t"<<resultFileNameSring<<endl<<"code type:\t"<<symbol->get_type_name()<<endl<<\ "decode string:\t"<<symbol->get_data()<<endl; image.set_data(NULL,0); //xml文件寫入 } } } waitKey(0); return 1; }
資源以下
http://download.csdn.net/detail/dengrengong/9461797