条形码检测(简单背景)
利用opencv来识别条形码,首先了解条形码的基本知识
条形码(barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符。
分析:
识别条形码与车牌类似,检测出图片中的垂直边缘并进行开操作从而形成联通区域,再进行腐蚀膨胀处理以便对条形码位置有更精准的定位。车牌识别可以根据长宽比来筛选,而条形码有不同规格则条形码长宽比不能成为筛选条件,而针对简单背景下的条形码,进行正确的图片处理操作后,最大的轮廓即为条形码区域。
思路:
- 读入图片并调整大小,以便处理不同的图片
- 对图片滤波,采用高斯滤波函数GaussianBlur
GaussianBlur(src,dst,Size(7,7),0);
根据不同需要可选用不同的内核大小,高斯滤波支持多通道单通道图片的输入,为sobel算子做准备
- 灰度化图片
- Sobel算子检测边缘
void Sobel(InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
int ksize=3,
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT )
第三个参数的意思为输出图像的深度,ddepth =-1时,代表输出图像与输入图像相同的深度。
dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘.
dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘.
当sobel算子ksize=3时选用Scharr效果更好。
下图说明了当dx=1,dy=0时检测的垂直边缘
2.下列代码为垂直边缘图减去水平边缘图,可以得到高垂直边缘的图片,滤掉水平边缘的干扰。
Sobel(img, ximg, CV_8UC1, 1, 0);
Sobel(img,yimg,CV_8UC1,0,1);
absdiff(ximg,yimg,res);
二值化图片,可选取较高阈值滤掉像素低的点,而原图是高垂直梯度所以可以保留垂直边缘而又过滤掉干扰因素。
开操作,腐蚀膨胀处理
形态学操作的对象是二值化图像.有名的形态学操作中包括腐蚀,膨胀,开操作,闭操作等。其中腐蚀,膨胀是许多形态学操作的基础。
这里首先进行开操作,开操作选取宽度大于长度的结构元素,滤掉明显干扰。然后进行腐蚀膨胀操作,消除条形码区的小斑点,腐蚀操作将会腐蚀图像中白色像素,以此来消除小斑点,而膨胀操作将使剩余的白色像素扩张并重新增长回去。
通过图片说明,左图是只进行开操作后结果不正确,定位错误,进行了一系列的腐蚀膨胀操作后,定位精准。
- 取轮廓并找出包围轮廓最大面积的矩形,即为条形码区.
部分结果图:
代码:
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("pic\\timg7.jpg",1);
Mat temp;
resize(src,temp, Size(550, 550));
Mat size;
GaussianBlur(temp,size,Size(7,7),0);
cvtColor(size,size,CV_BGR2GRAY);
Mat ximg, yimg, simg;
Scharr(size, ximg, CV_8U, 1, 0);
/*imshow("sobel垂直边缘", ximg);*/
Scharr(size,yimg, CV_8U, 0, 1);
absdiff(ximg,yimg,simg);
threshold(simg,simg,225,255,CV_THRESH_BINARY);
//threshold(simg,simg,0,255,CV_THRESH_BINARY+CV_THRESH_OTSU);
/*进行闭操作,使条形码连成一个联通区域*/
Mat element = getStructuringElement(MORPH_RECT,Size(23,3));
morphologyEx(simg, simg, MORPH_CLOSE, element);
Mat element5x5 = getStructuringElement(MORPH_RECT, Size(5, 5));
Mat element3x3 = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat element7x7 = getStructuringElement(MORPH_RECT, Size(7, 7));
erode(simg,simg,element7x7);
erode(simg, simg, element7x7);
erode(simg, simg, element5x5);
/*erode(simg, simg, element3x3);
erode(simg, simg, element3x3);
erode(simg, simg, element3x3);*/
dilate(simg,simg,element3x3);
dilate(simg, simg, element3x3);
dilate(simg, simg, element3x3);
dilate(simg, simg, element3x3);
dilate(simg, simg, element3x3);
dilate(simg, simg, element3x3);
imshow("sobel垂直边缘", simg);
vector<vector<Point>>contours;
findContours(simg,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
/*drawContours(Resize,contours,-1,Scalar(255,0,0),3,8);
找出最大的面积的矩形*/
int flag = 0;
double tem = 0;
for (int i = 0; i < contours.size();i++)
{
if (tem < fabs(contourArea(contours[i]))) {
tem = fabs(contourArea(contours[i]));
flag = i;
}
}
//画矩形
RotatedRect rect = minAreaRect(contours[flag]);
Point2f vect[4];
rect.points(vect);
for (int i = 0; i < 4; i++)
{
line(temp, vect[i], vect[(i + 1) % 4], Scalar(255, 0, 0), 2, 8);
}
namedWindow("dst", CV_WINDOW_AUTOSIZE);
imshow("dst", temp);
waitKey(0);
return 0;
}