Opencv+Zbar二維碼識別(標準條形碼/二維碼識別)
阿新 • • 發佈:2019-01-09
使用Opencv+Zbar組合可以很容易的識別圖片中的二維碼,特別是標準的二維碼,這裡標準指的是二維碼成像清晰,圖片中二維碼的空間佔比在40%~100%之間,這樣標準的圖片,Zbar識別起來很容易,不需要Opencv額外的處理。
下邊這個例程演示兩者配合對條形碼和二維碼的識別:
條形碼:#include "zbar.h" #include "cv.h" #include "highgui.h" #include <iostream> using namespace std; using namespace zbar; //新增zbar名稱空間 using namespace cv; int main(int argc,char*argv[]) { ImageScanner scanner; scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); Mat image = imread(argv[1]); Mat imageGray; cvtColor(image,imageGray,CV_RGB2GRAY); int width = imageGray.cols; int height = imageGray.rows; uchar *raw = (uchar *)imageGray.data; Image imageZbar(width, height, "Y800", raw, width * height); scanner.scan(imageZbar); //掃描條碼 Image::SymbolIterator symbol = imageZbar.symbol_begin(); if(imageZbar.symbol_begin()==imageZbar.symbol_end()) { cout<<"查詢條碼失敗,請檢查圖片!"<<endl; } for(;symbol != imageZbar.symbol_end();++symbol) { cout<<"型別:"<<endl<<symbol->get_type_name()<<endl<<endl; cout<<"條碼:"<<endl<<symbol->get_data()<<endl<<endl; } imshow("Source Image",image); waitKey(); imageZbar.set_data(NULL,0); return 0; }
二維碼:
這樣“標準的”二維碼是Zbar非常拿手的,能準確快速的檢測出來,包括在條形碼外有部分其他資訊的,也是小菜一碟:
Zbar很省心,我們還是可以為它做點什麼的,比如在一些情況下,需要把條形碼裁剪出來,這就涉及到條形碼位置的定位,這篇文章準備記錄一下如何定位條形碼,在定位之後再把裁剪出來的條形碼區域丟給Zbar識別讀碼。
方法一. 水平、垂直方向投影
#include "zbar.h" #include "cv.h" #include "highgui.h" #include <iostream> using namespace std; using namespace zbar; //新增zbar名稱空間 using namespace cv; //*********************************************** // 函式通過水平和垂直方向投影,找到兩個方向上投影的交叉矩形,定位到條形碼/二維碼 // int threshodValue 投影的最少畫素單位 // int binaryzationValue 原影象閾值分割值 //*********************************************** Rect DrawXYProjection(const Mat image,Mat &imageOut,const int threshodValue,const int binaryzationValue); int main(int argc,char*argv[]) { Mat image = imread(argv[1]); Mat imageCopy=image.clone(); Mat imageGray,imagOut; cvtColor(image,imageGray,CV_RGB2GRAY); Rect rect(0,0,0,0); rect= DrawXYProjection(image,imagOut,image.rows/10,100); Mat roi=image(rect); //畫出條形碼的矩形框 rectangle(imageCopy,Point(rect.x,rect.y),Point(rect.x+rect.width,rect.y+rect.height),Scalar(0,0,255),2); imshow("Source Image",image); imshow("水平垂直投影",imagOut); imshow("Output Image",roi); imshow("Source Image Rect",imageCopy); waitKey(); return 0; } Rect DrawXYProjection(const Mat image,Mat &imageOut,const int threshodValue,const int binaryzationValue) { Mat img=image.clone(); if(img.channels()>1) { cvtColor(img,img,CV_RGB2GRAY); } Mat out(img.size(),img.type(),Scalar(255)); imageOut=out; //對每一個傳入的圖片做灰度歸一化,以便使用同一套閾值引數 normalize(img,img,0,255,NORM_MINMAX); vector<int> vectorVertical(img.cols,0); for(int i=0;i<img.cols;i++) { for(int j=0;j<img.rows;j++) { if(img.at<uchar>(j,i)<binaryzationValue) { vectorVertical[i]++; } } } //列值歸一化 int high=img.rows/6; normalize(vectorVertical,vectorVertical,0,high,NORM_MINMAX); for(int i=0;i<img.cols;i++) { for(int j=0;j<img.rows;j++) { if(vectorVertical[i]>threshodValue) { line(imageOut,Point(i,img.rows),Point(i,img.rows-vectorVertical[i]),Scalar(0)); } } } //水平投影 vector<int> vectorHorizontal(img.rows,0); for(int i=0;i<img.rows;i++) { for(int j=0;j<img.cols;j++) { if(img.at<uchar>(i,j)<binaryzationValue) { vectorHorizontal[i]++; } } } normalize(vectorHorizontal,vectorHorizontal,0,high,NORM_MINMAX); for(int i=0;i<img.rows;i++) { for(int j=0;j<img.cols;j++) { if(vectorHorizontal[i]>threshodValue) { line(imageOut,Point(img.cols-vectorHorizontal[i],i),Point(img.cols,i),Scalar(0)); } } } //找到投影四個角點座標 vector<int>::iterator beginV=vectorVertical.begin(); vector<int>::iterator beginH=vectorHorizontal.begin(); vector<int>::iterator endV=vectorVertical.end()-1; vector<int>::iterator endH=vectorHorizontal.end()-1; int widthV=0; int widthH=0; int highV=0; int highH=0; while(*beginV<threshodValue) { beginV++; widthV++; } while(*endV<threshodValue) { endV--; widthH++; } while(*beginH<threshodValue) { beginH++; highV++; } while(*endH<threshodValue) { endH--; highH++; } //投影矩形 Rect rect(widthV,highV,img.cols-widthH-widthV,img.rows-highH-highV); return rect; }
通過影象在水平和垂直方向上的投影,按照一定的閾值,找到二維碼所在位置,剪切出來用於下一步Zbar條碼識別。當然這個方法只能識別出背景簡單的圖片中的二維碼。
條形碼效果:
水平、垂直投影
檢出條形碼區域
二維碼效果:
方法二.梯度運算
#include "core/core.hpp" #include "highgui/highgui.hpp" #include "imgproc/imgproc.hpp" using namespace cv; int main(int argc,char *argv[]) { Mat image,imageGray,imageGuussian; Mat imageSobelX,imageSobelY,imageSobelOut; image=imread(argv[1]); //1. 原影象大小調整,提高運算效率 resize(image,image,Size(500,300)); imshow("1.原影象",image); //2. 轉化為灰度圖 cvtColor(image,imageGray,CV_RGB2GRAY); imshow("2.灰度圖",imageGray); //3. 高斯平滑濾波 GaussianBlur(imageGray,imageGuussian,Size(3,3),0); imshow("3.高斯平衡濾波",imageGuussian); //4.求得水平和垂直方向灰度影象的梯度差,使用Sobel運算元 Mat imageX16S,imageY16S; Sobel(imageGuussian,imageX16S,CV_16S,1,0,3,1,0,4); Sobel(imageGuussian,imageY16S,CV_16S,0,1,3,1,0,4); convertScaleAbs(imageX16S,imageSobelX,1,0); convertScaleAbs(imageY16S,imageSobelY,1,0); imageSobelOut=imageSobelX-imageSobelY; imshow("4.X方向梯度",imageSobelX); imshow("4.Y方向梯度",imageSobelY); imshow("4.XY方向梯度差",imageSobelOut); //5.均值濾波,消除高頻噪聲 blur(imageSobelOut,imageSobelOut,Size(3,3)); imshow("5.均值濾波",imageSobelOut); //6.二值化 Mat imageSobleOutThreshold; threshold(imageSobelOut,imageSobleOutThreshold,180,255,CV_THRESH_BINARY); imshow("6.二值化",imageSobleOutThreshold); //7.閉運算,填充條形碼間隙 Mat element=getStructuringElement(0,Size(7,7)); morphologyEx(imageSobleOutThreshold,imageSobleOutThreshold,MORPH_CLOSE,element); imshow("7.閉運算",imageSobleOutThreshold); //8. 腐蝕,去除孤立的點 erode(imageSobleOutThreshold,imageSobleOutThreshold,element); imshow("8.腐蝕",imageSobleOutThreshold); //9. 膨脹,填充條形碼間空隙,根據核的大小,有可能需要2~3次膨脹操作 dilate(imageSobleOutThreshold,imageSobleOutThreshold,element); dilate(imageSobleOutThreshold,imageSobleOutThreshold,element); dilate(imageSobleOutThreshold,imageSobleOutThreshold,element); imshow("9.膨脹",imageSobleOutThreshold); vector<vector<Point>> contours; vector<Vec4i> hiera; //10.通過findContours找到條形碼區域的矩形邊界 findContours(imageSobleOutThreshold,contours,hiera,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); for(int i=0;i<contours.size();i++) { Rect rect=boundingRect((Mat)contours[i]); rectangle(image,rect,Scalar(255),2); } imshow("10.找出二維碼矩形區域",image); waitKey(); }
原影象
平滑濾波
水平和垂直方向灰度影象的梯度差
閉運算、腐蝕、膨脹後通過findContours找到條形碼區域的矩形邊界
二維碼:
原圖:
平衡濾波
梯度和
閉運算、腐蝕、膨脹後通過findContours找到條形碼區域的矩形邊界