c# EmguCv定位二维码
http://blog.csdn.net/gaobobo138968/article/details/47663607
Emgu 和opencv一样,用opencv写的程序当然也可以“翻译”为Emgu版,这里展示一个demo,使用Emgu定位图片中的二维码。
1.载入图像
System.Drawing.Image img = System.Drawing.Image.FromFile("E:\\code.jpg");
Bitmap barcodeBitmap = new Bitmap(img);
Image<Bgr, byte> img_src = new Image<Bgr, byte>(barcodeBitmap );
2.转为灰度图
Image<Gray, byte> imput_gray = new Image<Gray, byte>(img_src.Size);
CvInvoke.cvCvtColor(img_src, imput_gray, COLOR_CONVERSION.BGR2GRAY);
3.计算x,y方向梯度,相加
Image<Gray, byte> grad_x1 = new Image<Gray, byte>(img_src.Size);
Image<Gray, byte> grad_y1 = new Image<Gray, byte>(img_src.Size);
Image<Gray, byte> grad_all = new Image<Gray, byte>(img_src.Size);
CvInvoke.cvSobel(imput_gray, grad_x1, 0, 1,3);
CvInvoke.cvSobel(imput_gray, grad_y1, 1, 0, 3);
CvInvoke.cvAdd(grad_x1, grad_y1, grad_all, IntPtr.Zero);
梯度图:
4.平均模糊
//平均模糊(9*9内核大小)
CvInvoke.cvSmooth(grad_all, grad_all, SMOOTH_TYPE.CV_BLUR, 9, 9,0,0);
CvInvoke.cvShowImage("平均模糊", grad_all);
平均模糊结果:
5.二值化
CvInvoke.cvThreshold(grad_all, grad_all, 100, 255, THRESH.CV_THRESH_BINARY);
CvInvoke.cvShowImage("二值化", grad_all);
二值化图片:
6.消除裂缝
StructuringElementEx element = new StructuringElementEx( 21, 21,0, 0, Emgu.CV.CvEnum.CV_ELEMENT_SHAPE.CV_SHAPE_RECT );
CvInvoke.cvMorphologyEx( grad_all, grad_all, IntPtr.Zero, element, CV_MORPH_OP.CV_MOP_CLOSE, 1 );
CvInvoke.cvShowImage("消除裂缝", grad_all);
图片结果:
7.膨胀与腐蚀(消除杂点)
StructuringElementEx element1 = new StructuringElementEx(5, 5, 0, 0, Emgu.CV.CvEnum.CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
CvInvoke.cvErode(grad_all, grad_all, element1, 4);
CvInvoke.cvDilate(grad_all, grad_all, element1, 4);
CvInvoke.cvShowImage("膨胀与腐蚀", grad_all);
结果:
8.查找轮廓,绘制轮廓
IntPtr mem_storage = CvInvoke.cvCreateMemStorage(0);
MCvSeq first_contour = new MCvSeq();
IntPtr Dyncontour = new IntPtr();//存放检测到的图像块的首地址
CvInvoke.cvFindContours(grad_all, mem_storage, ref Dyncontour,
StructSize.MCvContour,
Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL,
Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE,
new Point(0, 0));
IntPtr temp = first_contour.ptr;
Seq<Point> DyncontourTemp1 = new Seq<Point>(Dyncontour, null);//方便对IntPtr类型进行操作
Seq<Point> DyncontourTemp = DyncontourTemp1;
for (; DyncontourTemp1 != null && DyncontourTemp1.Ptr.ToInt32() != 0; DyncontourTemp1 = DyncontourTemp1.HNext)
{
Rectangle rect = CvInvoke.cvBoundingRect(DyncontourTemp1, true);
if (rect.Width > 20 && rect.Height > 20)
{
rect.Width += 50;
rect.Height += 50;
Point p1 = new Point(rect.X, rect.Y);
Point p2 = new Point(rect.X + rect.Width, rect.Y + rect.Height);
CvInvoke.cvRectangle(img_src, p1, p2, new MCvScalar(0, 0, 255), 2, Emgu.CV.CvEnum.LINE_TYPE.EIGHT_CONNECTED, 0);
}
}
最后的结果:
9.最后
从上图可以看出找到了其中两个二维码,但是多了一个轮廓,不过定位二维码的主要功能就是用于识别,识别不出来的定位块自然也就没有用了。如果定位一维码(水平或垂直方向),即两个方向梯度相减,效果也不错。当然,这种定位方法会出现这样一些不必要的小框,是不是可以通过大小再过滤一次呢?