赞
踩
原模板匹配方法中,是无法进行任意角度和旋转匹配的,但我们在实际的使用中,模板图像的位置和方向都是不确定的,因此,就需要我们对模板图像进行预处理,处理后在进行模板匹配。
基本处理方法如下:
1:原图如下所示:
2:模板图像如下所示:
3:思路:在模板图像中,抠出图像,并且找到当前的旋转角度,将其恢复为正向0°,在进行对其模板匹配,给出匹配结果。处理过程图像如下:
分别是将其调整为正向后,0°,90°,180°,270°的旋转匹配,同时记录匹配度的值。
源码图下:
- public List<Point2d> GetResult(Mat tempImage, string srcImgPath, out double robotRotateAngel, out double arcVal)
- {
- List<Point2d> point2Ds = new List<Point2d>();
- robotRotateAngel = 999;
- arcVal = 0;
- try
- {
- double rotAngel;
- Point2f centerPoint;
- Rect rect;
- Mat tempMat = SetTemplateImage1(tempImage, out rotAngel, out rect, out centerPoint);
- bool matchRet = GetMatchRet1(srcImgPath, tempMat, rotAngel, out robotRotateAngel, out arcVal);
-
- if (!matchRet)
- {
- //图像识别失败
- return null;
- }
- }
- catch (Exception ex)
- {
-
- }
- return point2Ds;
- }
-
- public Mat SetTemplateImage1(Mat image, out double rotAngel, out Rect rect, out Point2f centerPoint)
- {
- rotAngel = -1;
- rect = new Rect();
- centerPoint = new Point2f();
- Mat blurImg = GaussBlurImage(image);//ip.MedBlurImage(image);//
- Mat cannyImg = new Mat();
- Cv2.Canny(blurImg, cannyImg, 30, 90);
- Mat morphImg = MorphImage(cannyImg);
- //Cv2.ImShow("morphImg1", morphImg);
- Mat roiImage = GetRioOutline(morphImg, image, out rotAngel, out rect, out centerPoint);
- //Cv2.ImShow("roiImage", roiImage);
- Mat rotImg = ImageRotate(roiImage, rotAngel, rect);
- //Cv2.ImShow("rotImg", rotImg);
- Mat mat = GetRoi(rotImg, rect);
- Cv2.ImShow("mat", mat);
- return mat;
- }
-
- /// <summary>
- /// 高斯 模糊
- /// </summary>
- /// <param name="img"></param>
- /// <returns></returns>
- public Mat GaussBlurImage(Mat image)
- {
- Mat gaussianimage = new Mat();
- Cv2.GaussianBlur(image, gaussianimage, new OpenCvSharp.Size(3, 3), 3, 3);//尽可能多的去除杂质
- return gaussianimage;
- }
-
- /// <summary>
- /// 图像形态学操作
- /// </summary>
- /// <param name="image"></param>
- /// <returns></returns>
- public Mat MorphImage(Mat image)
- {
- Mat morphImage = new Mat();
- Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3), new Point(-1, -1));
- Cv2.MorphologyEx(image, morphImage, MorphTypes.Gradient, kernel, new Point(-1, -1), 2);
- return morphImage;
- }
-
- /// <summary>
- /// 提取图像轮廓
- /// </summary>
- /// <param name="morphImage">形态学图片</param>
- /// <param name="srcImage">原图</param>
- /// <param name="rotateAngel">返回旋转角度</param>
- /// <param name="rect">返回获取的矩形边框</param>
- /// <returns></returns>
- public Mat GetRioOutline(Mat morphImage, Mat srcImage, out double rotateAngel, out Rect rect, out Point2f minRectcenterPoint)
- {
- OpenCvSharp.Point[][] contours;
- HierarchyIndex[] hierarchies;
- //Cv2.ImShow(",mmmm", morphImage);
- Cv2.FindContours(morphImage, out contours, out hierarchies, RetrievalModes.External, ContourApproximationModes.ApproxTC89KCOS, new Point());
- Mat connImg = Mat.Zeros(morphImage.Size(), MatType.CV_8UC3);
- rotateAngel = 0;
- //Point2f[] vertices = new Point2f[4];
- minRectcenterPoint = new Point2f();
- rect = new Rect();
- int index = 0;
- int count = -1;
- //寻找最大轮廓
- for (int i = 0; i < contours.Length; i++)
- {
- for (int j = 0; j < contours[i].Length; j++)
- {
- if (count < contours[i].Length)
- {
- count = contours[i].Length;
- index = i;
- }
- }
- }
- //获取轮廓点的矩形区域
- rect = Cv2.BoundingRect(contours[index]);
- //minAreaRect():以X轴正方形为起点,顺时针为正,逆时针为负
- //绘制Rio区域最小矩形
- #region 绘制Rio区域最小矩形
- RotatedRect minRect = Cv2.MinAreaRect(contours[index]);
- rotateAngel = minRect.Angle;
- minRectcenterPoint = minRect.Center;
- //vertices = minRect.Points();
- #endregion
- //绘制最小矩形
- #region 绘制最小矩形
- //Cv2.Line(srcImage, Convert.ToInt32(vertices[0].X), Convert.ToInt32(vertices[0].Y), Convert.ToInt32(vertices[1].X), Convert.ToInt32(vertices[1].Y), new Scalar(0, 255, 255));
- //Cv2.Line(srcImage, Convert.ToInt32(vertices[0].X), Convert.ToInt32(vertices[0].Y), Convert.ToInt32(vertices[3].X), Convert.ToInt32(vertices[3].Y), new Scalar(0, 255, 255));
- //Cv2.Line(srcImage, Convert.ToInt32(vertices[1].X), Convert.ToInt32(vertices[1].Y), Convert.ToInt32(vertices[2].X), Convert.ToInt32(vertices[2].Y), new Scalar(0, 255, 255));
- //Cv2.Line(srcImage, Convert.ToInt32(vertices[2].X), Convert.ToInt32(vertices[2].Y), Convert.ToInt32(vertices[3].X), Convert.ToInt32(vertices[3].Y), new Scalar(0, 255, 255));
- //Console.WriteLine("AngleRect_angle: {0}", minRect.Angle);
- #endregion
-
- //Cv2.ImShow("srcImage", srcImage);
-
- return srcImage;
- }
-
- /// <summary>
- /// 图像旋转
- /// </summary>
- /// <param name="image">旋转图像</param>
- /// <param name="rotateAngel">旋转角度</param>
- /// <returns></returns>
- public Mat ImageRotate(Mat image, double rotateAngel, Rect rect)
- {
- if (rotateAngel > -90 && rotateAngel <= -45)
- {
- rotateAngel += 90;
- }
- else if (!(rotateAngel > -45 && rotateAngel < 0))
- {
- //degree = degree;
- rotateAngel = 0;
- }
- //OpenCvSharp.Point center = new OpenCvSharp.Point(image.Cols / 2, image.Rows / 2);
- //以被截取的区域中心位置旋转
- //边缘放大截取
- OpenCvSharp.Point center = new OpenCvSharp.Point(rect.TopLeft.X + (rect.Width / 2), rect.TopLeft.Y + (rect.Height / 2));
- //截取最小矩形
- //OpenCvSharp.Point center = new OpenCvSharp.Point(image.Cols / 2, image.Rows / 2);
- double angle = rotateAngel;
- double scale = 1;
- Mat rotate = new Mat(2, 3, MatType.CV_32FC1);
- rotate = Cv2.GetRotationMatrix2D(center, angle, scale);//getRotationMatrix2D():以X轴正方形为起点,顺时针为负,逆时针为正
- Mat rotImage = new Mat();
- Cv2.WarpAffine(image, rotImage, rotate, image.Size(), InterpolationFlags.Linear, BorderTypes.Wrap);
- int winSize = 0;
- if (image.Cols > image.Rows)
- {
- winSize = image.Cols;
- }
- else
- {
- winSize = image.Rows;
- }
- return rotImage;
- }
-
-
- /// <summary>
- /// 获取感兴趣区域
- /// </summary>
- /// <param name="image">图像</param>
- /// <param name="rect">面积</param>
- /// <returns></returns>
- public Mat GetRoi(Mat image, Rect rect)
- {
- //Mat imageRoi = new Mat(image, new Rect(new Point(rect.TopLeft.X - rect.Width / 2, rect.TopLeft.Y), new Size(rect.Height, rect.Height)));
- Mat imageRoi = new Mat(image, new Rect(new Point(rect.TopLeft.X, rect.TopLeft.Y), new Size(rect.Width, rect.Height)));
- return imageRoi;
- }
-
- Dictionary<int, double> angelDic1;
- /// <summary>
- /// 模板匹配是否成功
- /// </summary>
- /// <param name="srcBmp">原图</param>
- /// <param name="templateMat">模板图像</param>
- /// <param name="rotAngel">初始旋转角度</param>
- /// <param name="robotRotateAngel">机器人需要旋转的角度</param>
- /// <returns></returns>
- public bool GetMatchRet1(string srcImgFilePath, Mat templateMat, double rotAngel, out double robotRotateAngel, out double arcValue)
- {
- double matchVal = 0;
- //最大旋转角度360
- robotRotateAngel = 999;
- arcValue = 0;
- try
- {
- Mat src = Cv2.ImRead(@"E:\SoftwarePackage\CodePackage\ImgPro\Img\T1.bmp");
- //src.ConvertTo(src, MatType.CV_8UC4);
- if (src.Empty())
- {
- //Console.WriteLine("src could not load image...\n");
- return false;
- }
- Cv2.ImShow("src", src);
- //Mat templateMat = Cv2.ImRead(@"E:\SoftwarePackage\CodePackage\ImgPro\Img\T21.bmp");
- if (templateMat.Empty())
- {
- //Console.WriteLine("src could not load image...\n");
- return false;
- }
-
- angelDic1 = new Dictionary<int, double>(); ;
- //记录角度
- int rotateAngel = 0;
- //第一张调正之后的图像
- //Cv2.ImShow(0.ToString(), mat);
- Point p;
- Bitmap rotateBmp;
- //旋转图像4个角度
- for (int i = 0; i < 4; i++)
- {
- if (templateMat.Rows > src.Rows || templateMat.Cols > src.Cols)
- {
- continue;
- }
- Bitmap bmp = MatchTemplate(templateMat, src, out p, out matchVal);
- if (matchVal > TemplateMatchThreshold)
- {
- angelDic1.Add(rotateAngel, matchVal);
- }
- //Mat rotImg1 = ip.ImageRotate(mat, 90);
- rotateBmp = BitmapConverter.ToBitmap(templateMat);
- rotateBmp.RotateFlip(RotateFlipType.Rotate90FlipXY);
- rotateAngel += 90;
- templateMat = BitmapConverter.ToMat(rotateBmp);
- if (1 + i > 3)
- {
- break;
- }
- Cv2.ImShow((i + 1).ToString(), templateMat);
- }
- //若匹配值没有符合条件
- if (angelDic1.Count < 1)
- {
- return false;
- }
-
- List<double> matchRet = new List<double>();
- foreach (KeyValuePair<int, double> kvp in angelDic1)
- {
- matchRet.Add(kvp.Value);
- }
- //判断匹配最大值是否大于设定阈值 否则 匹配失败
- //if (matchRet.Max() < TemplateMatchThreshold)
- //{
- // return false;
- //}
- //抠出模板图像 与原图进行模板匹配,并且记住最大匹配度的角度
- var ag = angelDic1.Where(q => q.Value == matchRet.Max()).Select(q => q.Key);
- robotRotateAngel = CalcRotateAngel(rotAngel, ag.Max());
- //角度转弧度
- arcValue = robotRotateAngel * arc;
- }
- catch (Exception ex)
- {
- //LogHelper.WriteLog(ex.Message, ex);
- }
- return true;
- }
-
- private double arc1 = Math.PI / 180;
-
- /// <summary>
- /// 模板匹配
- /// </summary>
- /// <param name="tempalte">模板图片像</param>
- /// <param name="srcPic">被匹配图像</param>
- /// <returns>标注匹配区域的图像</returns>
- public Bitmap MatchTemplate(Mat tempalte, Mat srcPic, out OpenCvSharp.Point tempPoint, out double matchValue)
- {
-
- using (Mat result = new Mat()) //匹配结果
- { //模板匹配
- Double minVul;
- Double maxVul;
- OpenCvSharp.Point minLoc = new OpenCvSharp.Point(0, 0);
- OpenCvSharp.Point maxLoc = new OpenCvSharp.Point(0, 0);
- OpenCvSharp.Point matchLoc = new OpenCvSharp.Point(0, 0);
- Cv2.MatchTemplate(srcPic, tempalte, result, TemplateMatchModes.CCoeffNormed);//CCoeffNormed 最好匹配为1,值越小匹配越差 xxxNormed的算法无需归一化
- //Cv2.Normalize(result, result, 0, 1, NormTypes.MinMax, -1);//归一化
- Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
- //Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc, null);//查找极值
- matchLoc = maxLoc;//最大值坐标
- //result.Set(matchLoc.Y, matchLoc.X, 0);//改变最大值为最小值
- Mat mask = srcPic.Clone();//复制整个矩阵
- //Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
- matchValue = maxVul;
- tempPoint.X = matchLoc.X;
- tempPoint.Y = matchLoc.Y;
- //画框显示 :对角线画框,起点和终点,都用Point,线宽
- if (matchValue > TemplateMatchThreshold)
- {
- Cv2.Rectangle(mask, matchLoc, new OpenCvSharp.Point(matchLoc.X + tempalte.Cols, matchLoc.Y + tempalte.Rows), Scalar.Green, 2);//2代表画的线条的宽细程度
- }
- return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mask);
- }
- }
-
- /// <summary>
- /// 计算角度
- /// </summary>
- /// <param name="srcAngel"></param>
- /// <param name="rotateAngle"></param>
- /// <returns></returns>
- public double CalcRotateAngel(double srcAngel, int rotateAngle)
- {
- //逆时针旋转为负 顺时针旋转为正
- double tempSrcAngel = Math.Abs(srcAngel);
- double resultAngel = -1;
- if (tempSrcAngel >= 45)
- {
- switch (rotateAngle)
- {
- case 0:
- resultAngel = 90 - tempSrcAngel;
- break;
- case 90:
- resultAngel = 180 - tempSrcAngel;
- break;
- case 180:
- //逆时针
- resultAngel = -(90 + tempSrcAngel);
- break;
- case 270:
- //逆时针
- resultAngel = -tempSrcAngel;
- break;
- }
- }
- else if (tempSrcAngel < 45)
- {
- switch (rotateAngle)
- {
- case 0:
- //逆时针
- resultAngel = -tempSrcAngel;
- break;
- case 90:
- resultAngel = 90 - tempSrcAngel;
- break;
- case 180:
- resultAngel = 90 + tempSrcAngel;
- break;
- case 270:
- //逆时针
- resultAngel = -(90 + tempSrcAngel);
- break;
- }
- }
- return resultAngel;
- }
完整代码都在上面了,感兴趣的小伙伴可以在优化优化。下个案例见。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。