基于opencv3.0和zbar下的条形码与二维码识别
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了基于opencv3.0和zbar下的条形码与二维码识别,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含15187字,纯文字阅读大概需要22分钟。
内容图文
其中对条码与二维码的识别分为以下4个步骤
1. 利用opencv和Zbar(或者Zxing)对标准的条形码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。
2. 利用opencv和Zbar(或者Zxing)对标准的QR二维码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。
3. 对非标准条形码,进行定位,然后用Zbar(或者Zxing)解码显示。
4. 对非标准的QR二维码图片,进行定位,然后用Zbar(或者Zxing)解码显示。
1. 利用opencv和Zbar(或者Zxing)对标准的条形码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。
2. 利用opencv和Zbar(或者Zxing)对标准的QR二维码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。
这两部对于zbar可以一并操作。
操作步骤主要分为两部分:A.原图进行灰度转化,B.送入Zbar扫描仪进行扫描(调用ImageScanner)
源码如下:
1 /* ***************************************************** 2 函数名称: Dis_Barcode 3 函数功能: 识别条形码和二维码 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明:这里是借鉴其他人的代码: 11 原文链接: https://www.cnblogs.com/dengxiaojun/p/5278679.html 12 以下代码是经过改动的 13 ***************************************************** */ 14 void MyClass::Dis_code(Mat image){ 15 Mat imageGray; // 所转化成的灰度图像 16//定义一个扫描仪 17 ImageScanner scanner; 18 scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); 1920 cvtColor(image, imageGray, CV_RGB2GRAY); 21 imshow("灰度图", imageGray); 22// 获取所摄取图像的长和宽 23int width = imageGray.cols; 24int height = imageGray.rows; 25// 在Zbar中进行扫描时候,需要将OpenCV中的Mat类型转换为(uchar *)类型,raw中存放的是图像的地址;对应的图像需要转成Zbar中对应的图像zbar::Image 26 uchar *raw = (uchar *)imageGray.data; 27 Image imageZbar(width, height, "Y800", raw, width * height); 28// 扫描相应的图像imageZbar(imageZbar是zbar::Image类型,存储着读入的图像) 29 scanner.scan(imageZbar); //扫描条码 30 Image::SymbolIterator symbol = imageZbar.symbol_begin(); 31if (imageZbar.symbol_begin() == imageZbar.symbol_end()) 32 { 33 cout << "查询条码失败,请检查图片!" << endl; 34 } 35for (; symbol != imageZbar.symbol_end(); ++symbol) 36 { 37 cout << "类型:" << endl << symbol->get_type_name() << endl << endl; 38 cout << "条码:" << endl << symbol->get_data() << endl << endl; 39 } 4041 waitKey(); // 等待按下esc键,若需要延时1s则改用waitKey(1000); 4243// 将图像中的数据置为0 44 imageZbar.set_data(NULL, 0); 45 system("pause"); 46 }
结果如下:
条形码识别:
二维码识别:
3. 对非标准条形码,进行定位,然后用Zbar(或者Zxing)解码显示
在条形码的识别上,根据条形码的特性,我们只关心x轴上的形态。通过x轴的宽度进行确定条码的大小,y轴根据实际提取进行区分
处理的目标:
A.消去非码的其他物体图形
B.划定条码的范围
C.提取图片的ROI区域(即条码区域)
总体分为:
灰度处理-》高斯平滑-》Sobel x—y梯度差-》均值滤波-》二值化-》闭运算-》腐蚀膨胀-》获取ROI
1 /* ***************************************************** 2 函数名称: Run 3 函数功能: 开始 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 void MyClass::Run(){ 13 Mat image; 14 image = getGray(srcimage);//获取灰度图15 image = getGass(image);//高斯平滑滤波16 image = getSobel(image);//Sobel x—y梯度差17 image = getBlur(image);//均值滤波除高频噪声18 image = getThold(image);//二值化19 image = getBys(image);//闭运算20 image = getErode(image);//腐蚀21 image = getDilate(image);//膨胀22 image = getRect(image, srcimage);//获取ROI23 imshow("最后的图", image); 24 Dis_code(image); 25 waitKey(); 26 }
灰度处理(消除颜色干扰)
1 /* ***************************************************** 2 函数名称: getGray 3 函数功能: 灰度处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getGray(Mat image, bool show){//show默认false 待定参数法13 Mat cimage; 14 cvtColor(image, cimage, CV_RGBA2GRAY); 15if (show) 16 imshow("灰度图", cimage); 17return cimage; 18 }
处理结果:
高斯滤波处理(消除高斯噪声)
1 /* ***************************************************** 2 函数名称: getGass 3 函数功能: 高斯滤波处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getGass(Mat image, bool show){ 13 Mat cimage; 14 GaussianBlur(image, cimage, Size(3, 3), 0); 15if (show) 16 imshow("高斯滤波图", cimage); 17return cimage; 18 }
处理结果:
Sobel x-y差处理(只考虑x轴,消除y轴不必要信息)
1 /* ***************************************************** 2 函数名称: getSobel 3 函数功能: Sobel处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getSobel(Mat image, bool show){ 13 Mat cimageX16s, cimageY16s, imageSobelX, imageSobelY, out; 14 Sobel(image, cimageX16s, CV_16S, 1, 0, 3, 1, 0, 4); 15 Sobel(image, cimageY16s, CV_16S, 0, 1, 3, 1, 0, 4); 16 convertScaleAbs(cimageX16s, imageSobelX, 1, 0); 17 convertScaleAbs(cimageY16s, imageSobelY, 1, 0); 18out = imageSobelX - imageSobelY; 19if (show) 20 imshow("Sobelx-y差 图", out); 21returnout; 22 }
处理结果:
均值滤波处理(消除高频噪声)
1 /* ***************************************************** 2 函数名称: getBlur 3 函数功能: 均值滤波处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getBlur(Mat image, bool show){ 13 Mat cimage; 14 blur(image, cimage, Size(3, 3)); 15if (show) 16 imshow("均值滤波图", cimage); 17return cimage; 18 }
处理结果:
二值化处理(使图像中数据量大为减少,从而能凸显出目标的轮廓)
1 /* ***************************************************** 2 函数名称: getThold 3 函数功能: 二值化处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getThold(Mat image, bool show){ 13 Mat cimage; 14 threshold(image, cimage, 112, 255, CV_THRESH_BINARY); 15if (show) 16 imshow("二值化图", cimage); 17return cimage; 18 }
处理结果:
闭运算处理(扩大轴之间的间隙)
1 /* ***************************************************** 2 函数名称: getBys 3 函数功能: 闭运算处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getBys(Mat image, bool show){ 13 morphologyEx(image, image, MORPH_CLOSE, element); 14if (show) 15 imshow("闭运算图", image); 16return image; 17 }
处理结果:
腐蚀膨胀(消去干扰点和合并条码区域)
1 /* ***************************************************** 2 函数名称: getErode 3 函数功能: 腐蚀处理 4 传入参数: Mat image 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 Mat MyClass::getErode(Mat image, bool show){ 13//Mat cimage;14 erode(image, image, element); 15if (show) 16 imshow("腐蚀图", image); 17return image; 18} 19/****************************************************** 20函数名称: getDilate 21函数功能: 膨胀处理 22传入参数: Mat image 23返 回 值: 24建立时间: 2018-05-19 25修改时间: 26建 立 人: 27修 改 人: 28其它说明: 29******************************************************/30 Mat MyClass::getDilate(Mat image, bool show){ 31for (int i = 0; i < 3; i++) 32 dilate(image, image, element); 33if (show) 34 imshow("膨胀图", image); 35return image; 36 }
处理结果:
获取ROI(为Zbar处理作预处理)
1 /* ***************************************************** 2 函数名称: getRect 3 函数功能: 获取码的区域 4 传入参数: Mat image, Mat simage原图 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明:借鉴其他人进行改进 11 ***************************************************** */ 12 Mat MyClass::getRect(Mat image, Mat simage, bool show){ 13 vector<vector<Point>> contours; 14 vector<Vec4i> hiera; 15 Mat cimage; 16 findContours(image, contours, hiera, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); 17 vector<float>contourArea; 18for (int i = 0; i < contours.size(); i++) 19 { 20 contourArea.push_back(cv::contourArea(contours[i])); 21 } 22//找出面积最大的轮廓23double maxValue; Point maxLoc; 24 minMaxLoc(contourArea, NULL, &maxValue, NULL, &maxLoc); 25//计算面积最大的轮廓的最小的外包矩形26 RotatedRect minRect = minAreaRect(contours[maxLoc.x]); 27//为了防止找错,要检查这个矩形的偏斜角度不能超标 28//如果超标,那就是没找到29if (minRect.angle<2.0) 30 { 31//找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形32 Rect myRect = boundingRect(contours[maxLoc.x]); 33//把这个矩形在源图像中画出来 34//rectangle(srcImage,myRect,Scalar(0,255,255),3,LINE_AA); 35//看看显示效果,找的对不对 36//imshow(windowNameString,srcImage); 37//将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张38 myRect.x = myRect.x - (myRect.width / 20); 39 myRect.width = myRect.width*1.1; 40 Mat resultImage = Mat(srcimage, myRect); 41return resultImage; 42 } 4344for (int i = 0; i<contours.size(); i++) 45 { 46 Rect rect = boundingRect((Mat)contours[i]); 47//cimage = simage(rect);48 rectangle(simage, rect, Scalar(0), 2); 49if (show) 50 imshow("转变图", simage); 51 } 52return simage; 53 }
处理结果:
最后识别处理结果:
4. 对非标准的QR二维码图片,进行定位,然后用Zbar(或者Zxing)解码显示。
这里主要参考https://blog.csdn.net/nick123chao/article/details/77573675的博客。不过该博客的处理没有考虑多个识别点时的情况:
例图:
本文主要处理去除干扰的识别点的方向进行研究解决。根据二维码特性:
我们只要找到90°±Δx的角,且夹角两边为最小的边即可。
找到三个点后,我们需要对齐做旋转处理,旋转的角度如下:
其中处理的步骤分为:
灰度处理-》边缘检测-》特征轮廓检测-》提取特征点-》排除干扰点-》绘制直角三角形-》纠正旋转-》提取ROI-》识别
这里先给效果,后展示代码
边缘检测:
特征轮廓检测
提取特征点-》排除干扰点-》绘制直角三角形
纠正旋转
提取ROI
识别
源码如下:
1 /* ***************************************************** 2 函数名称: QrRun 3 函数功能: 开始 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-19 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ***************************************************** */ 12 void MyClass::QrRun(){ 13 RNG rng(12345); 14//imshow("原图", srcimage); 15 Mat src_all = srcimage.clone(); 16 Mat src_gray; 17//灰度处理 18 src_gray = getBlur(getGray(srcimage)); 19 20 Scalar color = Scalar(1, 1, 255); 21 Mat threshold_output; 22 vector<vector<Point> > contours, contours2; 23 vector<Vec4i> hierarchy; 24 Mat drawing = Mat::zeros(srcimage.size(), CV_8UC3); 25 Mat drawing2 = Mat::zeros(srcimage.size(), CV_8UC3); 26 Mat drawingAllContours = Mat::zeros(srcimage.size(), CV_8UC3); 27 28 threshold_output = getThold(src_gray); 29 30 findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0)); 31 32int c = 0, ic = 0, k = 0, area = 0; 33// 边缘检测 34//通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角 35int parentIdx = -1; 36for (int i = 0; i< contours.size(); i++) 37 { 38//画出所以轮廓图 39 drawContours(drawingAllContours, contours, parentIdx, CV_RGB(255, 255, 255), 1, 8); 40if (hierarchy[i][2] != -1 && ic == 0) 41 { 42 parentIdx = i; 43 ic++; 44 } 45elseif (hierarchy[i][2] != -1) 46 { 47 ic++; 48 } 49elseif (hierarchy[i][2] == -1) 50 { 51 ic = 0; 52 parentIdx = -1; 53 } 54//特征轮廓检测 - 》 55//有两个子轮廓 56if (ic >= 2) 57 { 58//保存找到的三个黑色定位角 59 contours2.push_back(contours[parentIdx]); 60//画出三个黑色定位角的轮廓 61 drawContours(drawing, contours, parentIdx, CV_RGB(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, 8); 62 ic = 0; 63 parentIdx = -1; 64 } 65 } 66//提取特征点 67//填充的方式画出黑色定位角的轮廓 68for (int i = 0; i<contours2.size(); i++) 69 drawContours(drawing2, contours2, i, CV_RGB(rng.uniform(100, 255), rng.uniform(100, 255), rng.uniform(100, 255)), -1, 4, hierarchy[k][2], 0, Point()); 70 71//获取定位角的中心坐标 72 vector<Point> pointfind; 73for (int i = 0; i<contours2.size(); i++) 74 { 75 pointfind.push_back(Center_cal(contours2, i)); 76 } 77//排除干扰点 78 Mat dst; 79 Point point[3]; double angle; Mat rot_mat; 80///选择合适的点-核心筛选 81if (pointfind.size()>3){ 82double lengthA = 10000000000000000, lengthB = 10000000000000000000; 83for (int i = 0; i < pointfind.size(); i++){ 84for (int j = 0; j < pointfind.size(); j++){ 85for (int k = 0; k < pointfind.size(); k++){ 86if (i != j&&j != k&&i != k){ 87double dxa, dxb,dya,dyb; 88double k1, k2, wa, wb; 89 dxa = pointfind[i].x - pointfind[j].x; 90 dxb = pointfind[i].x - pointfind[k].x; 91 dya = pointfind[i].y - pointfind[j].y; 92 dyb = pointfind[i].y - pointfind[k].y; 93if (dxa == 0 || dxb == 0)continue; 94 k1 = dya/dxa; 95 k2 = dyb/dxb ; 96 wa = sqrt(pow(dya, 2) + pow(dya, 2)); 97 wb = sqrt(pow(dyb, 2) + pow(dxb, 2)); 98double anglea = abs(atan(k1) * 180 / CV_PI) + abs(atan(k2) * 180 / CV_PI); 99if (int(anglea)>=85&&int(anglea)<=95&&wa<=lengthA&&wb<=lengthB){ 100 lengthA = wa; 101 lengthB = wb; 102 point[0] = pointfind[i]; 103 point[1] = pointfind[j]; 104 point[2] = pointfind[k]; 105 } 106 } 107 } 108 } 109 } 110 } 111else{ 112for (int i = 0; i < 3; i++){ 113 point[i] = pointfind[i]; 114 } 115 } 116//绘制直角三角形 117//计算轮廓的面积,计算定位角的面积,从而计算出边长 118 area = contourArea(contours2[0]); 119int area_side = cvRound(sqrt(double(area))); 120for (int i = 0; i < 3; i++){ 121 line(drawing2, point[i], point[(i + 1)%3], color, area_side / 2, 8); 122 } 123124//纠正旋转 125//判断是否正对126if (!IsCorrect(point)){ 127//进入修正环节128double angle; Mat rot_mat; 129int start = 0; 130for (int i = 0; i < 3; i++){ 131double k1, k2,kk; 132 k1 = (point[i].y - point[(i + 1) % 3].y) / (point[i].x - point[(i + 1) % 3].x); 133 k2 = (point[i].y - point[(i + 2) % 3].y) / (point[i].x - point[(i + 2) % 3].x); 134 kk = k1*k2; 135if (k1*k2 <0) 136 start = i; 137 } 138double ax, ay, bx, by; 139 ax = point[(start + 1) % 3].x; 140 ay = point[(start + 1) % 3].y; 141 bx = point[(start + 2) % 3].x; 142 by = point[(start + 2) % 3].y; 143 Point2f center(abs(ax - bx) / 2, abs(ay -by)/ 2); 144double dy = ay - by; 145double dx = ax - bx; 146double k3 = dy / dx; 147 angle =atan(k3) * 180 / CV_PI;//转化角度148 rot_mat = getRotationMatrix2D(center, angle, 1.0); 149150 warpAffine(src_all, dst, rot_mat, src_all.size(), 1, 0, 0);//旋转原图查看151 warpAffine(drawing2, drawing2, rot_mat, src_all.size(), 1, 0, 0);//旋转连线图152 warpAffine(src_all, src_all, rot_mat, src_all.size(), 1, 0, 0);//旋转原图153154 namedWindow("Dst"); 155 imshow("Dst", dst); 156 } 157158 namedWindow("DrawingAllContours"); 159 imshow("DrawingAllContours", drawingAllContours); 160161 namedWindow("Drawing2"); 162 imshow("Drawing2", drawing2); 163164 namedWindow("Drawing"); 165 imshow("Drawing", drawing); 166167//提取ROI 168//接下来要框出这整个二维码 169 Mat gray_all, threshold_output_all; 170 vector<vector<Point> > contours_all; 171 vector<Vec4i> hierarchy_all; 172 cvtColor(drawing2, gray_all, CV_BGR2GRAY); 173174175 threshold(gray_all, threshold_output_all, 45, 255, THRESH_BINARY); 176 findContours(threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//RETR_EXTERNAL表示只寻找最外层轮廓 177178179 Point2f fourPoint2f[4]; 180//求最小包围矩形 181 RotatedRect rectPoint = minAreaRect(contours_all[1]);//pointfind.size()-3 182183//将rectPoint变量中存储的坐标值放到 fourPoint的数组中 184 rectPoint.points(fourPoint2f); 185186int maxx = 0, maxy = 0, minx = 100000, miny = 100000; 187for (int i = 0; i < 4; i++) 188 { 189if (maxx < fourPoint2f[i].x)maxx = fourPoint2f[i].x; 190if (maxy < fourPoint2f[i].y)maxy = fourPoint2f[i].y; 191if (minx > fourPoint2f[i].x)minx = fourPoint2f[i].x; 192if (miny > fourPoint2f[i].y)miny = fourPoint2f[i].y; 193 line(src_all, fourPoint2f[i % 4], fourPoint2f[(i + 1) % 4] 194 , Scalar(0), 3); 195 } 196 namedWindow("Src_all"); 197///边际处理198int set_inter = 5; 199while (true) 200 { 201 minx -= set_inter; 202 miny -= set_inter; 203 maxx += set_inter; 204 maxy += set_inter; 205if (maxx > srcimage.size().width || maxy > srcimage.size().height || minx < 0 || miny < 0){ 206 minx += set_inter; 207 miny += set_inter; 208 maxx -= set_inter; 209 maxy -= set_inter; 210 set_inter--; 211 } 212else213 { 214break; 215 } 216 } 217 imshow("Src_all", src_all(Rect(minx, miny, maxx - minx, maxy - miny)));//ROI218 Mat fout = src_all(Rect(minx, miny, maxx - minx, maxy - miny));//ROI 219220//识别221 Dis_code(fout); 222223 waitKey(0); 224 destroyAllWindows(); 225 }
由于在解码上是采用其他人的方法,存在解码问题。(到时候有机会自己再写下)
ps:目前的zbar不支持中文识别,但是zxing可以。所以借鉴本文的需要改进下识别的模块即可。
本文的不足之处:
这里还做了测试,对于旋转180°以上的二维码图片存在可能无法识别的问题。以及码眼为非正方形的也无法识别。
如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/Code_Find进行源码克隆下载
如有问题请留言评论。转载请注明出处,谢谢。
原文:https://www.cnblogs.com/cjqbaba/p/9073908.html
内容总结
以上是互联网集市为您收集整理的基于opencv3.0和zbar下的条形码与二维码识别全部内容,希望文章能够帮你解决基于opencv3.0和zbar下的条形码与二维码识别所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。