二维码和车牌识别基本都会涉及到图像的校正,主要是形变和倾斜角度的校正,一种二维码的畸变如下图:
这个码用微信扫了一下,识别不出来,但是用Zbar还是可以准确识别的~~。
这里介绍一种二维码校正方法,通过定位二维码的4个顶点,利用仿射变换校正。基本思路:滤波->二值化->膨胀(腐蚀)操作->形态学边界->寻找直线->定位交点->仿射变换校正->Zbar识别。
滤波、二值化:
腐蚀操作:
形态学边界:
寻找直线:
角点定位:
仿射变换校正:
Zbar识别:
Code实现:
#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[])
{
Mat imageSource=imread(argv[1],0);
Mat image;
imageSource.copyTo(image);
GaussianBlur(image,image,Size(3,3),0); //滤波
threshold(image,image,100,255,CV_THRESH_BINARY); //二值化
imshow("二值化",image);
Mat element=getStructuringElement(2,Size(7,7)); //膨胀腐蚀核
//morphologyEx(image,image,MORPH_OPEN,element);
for(int i=0;i<10;i++)
{
erode(image,image,element);
i++;
}
imshow("腐蚀s",image);
Mat image1;
erode(image,image1,element);
image1=image-image1;
imshow("边界",image1);
//寻找直线 边界定位也可以用findContours实现
vector<Vec2f>lines;
HoughLines(image1,lines,1,CV_PI/150,250,0,0);
Mat DrawLine=Mat::zeros(image1.size(),CV_8UC1);
for(int i=0;i<lines.size();i++)
{
float rho=lines[i][0];
float theta=lines[i][1];
Point pt1,pt2;
double a=cos(theta),b=sin(theta);
double x0=a*rho,y0=b*rho;
pt1.x=cvRound(x0+1000*(-b));
pt1.y=cvRound(y0+1000*a);
pt2.x=cvRound(x0-1000*(-b));
pt2.y=cvRound(y0-1000*a);
line(DrawLine,pt1,pt2,Scalar(255),1,CV_AA);
}
imshow("直线",DrawLine);
Point2f P1[4];
Point2f P2[4];
vector<Point2f>corners;
goodFeaturesToTrack(DrawLine,corners,4,0.1,10,Mat()); //角点检测
for(int i=0;i<corners.size();i++)
{
circle(DrawLine,corners[i],3,Scalar(255),3);
P1[i]=corners[i];
}
imshow("交点",DrawLine);
int width=P1[1].x-P1[0].x;
int hight=P1[2].y-P1[0].y;
P2[0]=P1[0];
P2[1]=Point2f(P2[0].x+width,P2[0].y);
P2[2]=Point2f(P2[0].x,P2[1].y+hight);
P2[3]=Point2f(P2[1].x,P2[2].y);
Mat elementTransf;
elementTransf= getAffineTransform(P1,P2);
warpAffine(imageSource,imageSource,elementTransf,imageSource.size(),1,0,Scalar(255));
imshow("校正",imageSource);
//Zbar二维码识别
ImageScanner scanner;
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
int width1 = imageSource.cols;
int height1 = imageSource.rows;
uchar *raw = (uchar *)imageSource.data;
Image imageZbar(width1, height1, "Y800", raw, width * height1);
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;
}
namedWindow("Source Window",0);
imshow("Source Window",imageSource);
waitKey();
imageZbar.set_data(NULL,0);
return 0;
}