当前位置:   article > 正文

OpenCv案例(十三):基于OpenCVSharp-模板匹之旋转角度匹配

opencvsharp

模板匹配方法中,是无法进行任意角度和旋转匹配的,但我们在实际的使用中,模板图像的位置和方向都是不确定的,因此,就需要我们对模板图像进行预处理,处理后在进行模板匹配。

基本处理方法如下:

  • 模板图像读取
  • 进行模糊
  • 绘制轮廓
  • 形态学处理
  • 提取轮廓
  • 获取当前位置角度
  • 图像旋转。
  • 原图读取
  • 从4个角度进行模板匹配(0°,90°,180°,270°)
  • 记录每个角度的匹配值
  • 进行后续操作。

1:原图如下所示:

2:模板图像如下所示:

3:思路:在模板图像中,抠出图像,并且找到当前的旋转角度,将其恢复为正向0°,在进行对其模板匹配,给出匹配结果。处理过程图像如下:

分别是将其调整为正向后,0°,90°,180°,270°的旋转匹配,同时记录匹配度的值。

 

源码图下:

  1. public List<Point2d> GetResult(Mat tempImage, string srcImgPath, out double robotRotateAngel, out double arcVal)
  2. {
  3. List<Point2d> point2Ds = new List<Point2d>();
  4. robotRotateAngel = 999;
  5. arcVal = 0;
  6. try
  7. {
  8. double rotAngel;
  9. Point2f centerPoint;
  10. Rect rect;
  11. Mat tempMat = SetTemplateImage1(tempImage, out rotAngel, out rect, out centerPoint);
  12. bool matchRet = GetMatchRet1(srcImgPath, tempMat, rotAngel, out robotRotateAngel, out arcVal);
  13. if (!matchRet)
  14. {
  15. //图像识别失败
  16. return null;
  17. }
  18. }
  19. catch (Exception ex)
  20. {
  21. }
  22. return point2Ds;
  23. }
  24. public Mat SetTemplateImage1(Mat image, out double rotAngel, out Rect rect, out Point2f centerPoint)
  25. {
  26. rotAngel = -1;
  27. rect = new Rect();
  28. centerPoint = new Point2f();
  29. Mat blurImg = GaussBlurImage(image);//ip.MedBlurImage(image);//
  30. Mat cannyImg = new Mat();
  31. Cv2.Canny(blurImg, cannyImg, 30, 90);
  32. Mat morphImg = MorphImage(cannyImg);
  33. //Cv2.ImShow("morphImg1", morphImg);
  34. Mat roiImage = GetRioOutline(morphImg, image, out rotAngel, out rect, out centerPoint);
  35. //Cv2.ImShow("roiImage", roiImage);
  36. Mat rotImg = ImageRotate(roiImage, rotAngel, rect);
  37. //Cv2.ImShow("rotImg", rotImg);
  38. Mat mat = GetRoi(rotImg, rect);
  39. Cv2.ImShow("mat", mat);
  40. return mat;
  41. }
  42. /// <summary>
  43. /// 高斯 模糊
  44. /// </summary>
  45. /// <param name="img"></param>
  46. /// <returns></returns>
  47. public Mat GaussBlurImage(Mat image)
  48. {
  49. Mat gaussianimage = new Mat();
  50. Cv2.GaussianBlur(image, gaussianimage, new OpenCvSharp.Size(3, 3), 3, 3);//尽可能多的去除杂质
  51. return gaussianimage;
  52. }
  53. /// <summary>
  54. /// 图像形态学操作
  55. /// </summary>
  56. /// <param name="image"></param>
  57. /// <returns></returns>
  58. public Mat MorphImage(Mat image)
  59. {
  60. Mat morphImage = new Mat();
  61. Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3), new Point(-1, -1));
  62. Cv2.MorphologyEx(image, morphImage, MorphTypes.Gradient, kernel, new Point(-1, -1), 2);
  63. return morphImage;
  64. }
  65. /// <summary>
  66. /// 提取图像轮廓
  67. /// </summary>
  68. /// <param name="morphImage">形态学图片</param>
  69. /// <param name="srcImage">原图</param>
  70. /// <param name="rotateAngel">返回旋转角度</param>
  71. /// <param name="rect">返回获取的矩形边框</param>
  72. /// <returns></returns>
  73. public Mat GetRioOutline(Mat morphImage, Mat srcImage, out double rotateAngel, out Rect rect, out Point2f minRectcenterPoint)
  74. {
  75. OpenCvSharp.Point[][] contours;
  76. HierarchyIndex[] hierarchies;
  77. //Cv2.ImShow(",mmmm", morphImage);
  78. Cv2.FindContours(morphImage, out contours, out hierarchies, RetrievalModes.External, ContourApproximationModes.ApproxTC89KCOS, new Point());
  79. Mat connImg = Mat.Zeros(morphImage.Size(), MatType.CV_8UC3);
  80. rotateAngel = 0;
  81. //Point2f[] vertices = new Point2f[4];
  82. minRectcenterPoint = new Point2f();
  83. rect = new Rect();
  84. int index = 0;
  85. int count = -1;
  86. //寻找最大轮廓
  87. for (int i = 0; i < contours.Length; i++)
  88. {
  89. for (int j = 0; j < contours[i].Length; j++)
  90. {
  91. if (count < contours[i].Length)
  92. {
  93. count = contours[i].Length;
  94. index = i;
  95. }
  96. }
  97. }
  98. //获取轮廓点的矩形区域
  99. rect = Cv2.BoundingRect(contours[index]);
  100. //minAreaRect():以X轴正方形为起点,顺时针为正,逆时针为负
  101. //绘制Rio区域最小矩形
  102. #region 绘制Rio区域最小矩形
  103. RotatedRect minRect = Cv2.MinAreaRect(contours[index]);
  104. rotateAngel = minRect.Angle;
  105. minRectcenterPoint = minRect.Center;
  106. //vertices = minRect.Points();
  107. #endregion
  108. //绘制最小矩形
  109. #region 绘制最小矩形
  110. //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));
  111. //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));
  112. //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));
  113. //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));
  114. //Console.WriteLine("AngleRect_angle: {0}", minRect.Angle);
  115. #endregion
  116. //Cv2.ImShow("srcImage", srcImage);
  117. return srcImage;
  118. }
  119. /// <summary>
  120. /// 图像旋转
  121. /// </summary>
  122. /// <param name="image">旋转图像</param>
  123. /// <param name="rotateAngel">旋转角度</param>
  124. /// <returns></returns>
  125. public Mat ImageRotate(Mat image, double rotateAngel, Rect rect)
  126. {
  127. if (rotateAngel > -90 && rotateAngel <= -45)
  128. {
  129. rotateAngel += 90;
  130. }
  131. else if (!(rotateAngel > -45 && rotateAngel < 0))
  132. {
  133. //degree = degree;
  134. rotateAngel = 0;
  135. }
  136. //OpenCvSharp.Point center = new OpenCvSharp.Point(image.Cols / 2, image.Rows / 2);
  137. //以被截取的区域中心位置旋转
  138. //边缘放大截取
  139. OpenCvSharp.Point center = new OpenCvSharp.Point(rect.TopLeft.X + (rect.Width / 2), rect.TopLeft.Y + (rect.Height / 2));
  140. //截取最小矩形
  141. //OpenCvSharp.Point center = new OpenCvSharp.Point(image.Cols / 2, image.Rows / 2);
  142. double angle = rotateAngel;
  143. double scale = 1;
  144. Mat rotate = new Mat(2, 3, MatType.CV_32FC1);
  145. rotate = Cv2.GetRotationMatrix2D(center, angle, scale);//getRotationMatrix2D():以X轴正方形为起点,顺时针为负,逆时针为正
  146. Mat rotImage = new Mat();
  147. Cv2.WarpAffine(image, rotImage, rotate, image.Size(), InterpolationFlags.Linear, BorderTypes.Wrap);
  148. int winSize = 0;
  149. if (image.Cols > image.Rows)
  150. {
  151. winSize = image.Cols;
  152. }
  153. else
  154. {
  155. winSize = image.Rows;
  156. }
  157. return rotImage;
  158. }
  159. /// <summary>
  160. /// 获取感兴趣区域
  161. /// </summary>
  162. /// <param name="image">图像</param>
  163. /// <param name="rect">面积</param>
  164. /// <returns></returns>
  165. public Mat GetRoi(Mat image, Rect rect)
  166. {
  167. //Mat imageRoi = new Mat(image, new Rect(new Point(rect.TopLeft.X - rect.Width / 2, rect.TopLeft.Y), new Size(rect.Height, rect.Height)));
  168. Mat imageRoi = new Mat(image, new Rect(new Point(rect.TopLeft.X, rect.TopLeft.Y), new Size(rect.Width, rect.Height)));
  169. return imageRoi;
  170. }
  171. Dictionary<int, double> angelDic1;
  172. /// <summary>
  173. /// 模板匹配是否成功
  174. /// </summary>
  175. /// <param name="srcBmp">原图</param>
  176. /// <param name="templateMat">模板图像</param>
  177. /// <param name="rotAngel">初始旋转角度</param>
  178. /// <param name="robotRotateAngel">机器人需要旋转的角度</param>
  179. /// <returns></returns>
  180. public bool GetMatchRet1(string srcImgFilePath, Mat templateMat, double rotAngel, out double robotRotateAngel, out double arcValue)
  181. {
  182. double matchVal = 0;
  183. //最大旋转角度360
  184. robotRotateAngel = 999;
  185. arcValue = 0;
  186. try
  187. {
  188. Mat src = Cv2.ImRead(@"E:\SoftwarePackage\CodePackage\ImgPro\Img\T1.bmp");
  189. //src.ConvertTo(src, MatType.CV_8UC4);
  190. if (src.Empty())
  191. {
  192. //Console.WriteLine("src could not load image...\n");
  193. return false;
  194. }
  195. Cv2.ImShow("src", src);
  196. //Mat templateMat = Cv2.ImRead(@"E:\SoftwarePackage\CodePackage\ImgPro\Img\T21.bmp");
  197. if (templateMat.Empty())
  198. {
  199. //Console.WriteLine("src could not load image...\n");
  200. return false;
  201. }
  202. angelDic1 = new Dictionary<int, double>(); ;
  203. //记录角度
  204. int rotateAngel = 0;
  205. //第一张调正之后的图像
  206. //Cv2.ImShow(0.ToString(), mat);
  207. Point p;
  208. Bitmap rotateBmp;
  209. //旋转图像4个角度
  210. for (int i = 0; i < 4; i++)
  211. {
  212. if (templateMat.Rows > src.Rows || templateMat.Cols > src.Cols)
  213. {
  214. continue;
  215. }
  216. Bitmap bmp = MatchTemplate(templateMat, src, out p, out matchVal);
  217. if (matchVal > TemplateMatchThreshold)
  218. {
  219. angelDic1.Add(rotateAngel, matchVal);
  220. }
  221. //Mat rotImg1 = ip.ImageRotate(mat, 90);
  222. rotateBmp = BitmapConverter.ToBitmap(templateMat);
  223. rotateBmp.RotateFlip(RotateFlipType.Rotate90FlipXY);
  224. rotateAngel += 90;
  225. templateMat = BitmapConverter.ToMat(rotateBmp);
  226. if (1 + i > 3)
  227. {
  228. break;
  229. }
  230. Cv2.ImShow((i + 1).ToString(), templateMat);
  231. }
  232. //若匹配值没有符合条件
  233. if (angelDic1.Count < 1)
  234. {
  235. return false;
  236. }
  237. List<double> matchRet = new List<double>();
  238. foreach (KeyValuePair<int, double> kvp in angelDic1)
  239. {
  240. matchRet.Add(kvp.Value);
  241. }
  242. //判断匹配最大值是否大于设定阈值 否则 匹配失败
  243. //if (matchRet.Max() < TemplateMatchThreshold)
  244. //{
  245. // return false;
  246. //}
  247. //抠出模板图像 与原图进行模板匹配,并且记住最大匹配度的角度
  248. var ag = angelDic1.Where(q => q.Value == matchRet.Max()).Select(q => q.Key);
  249. robotRotateAngel = CalcRotateAngel(rotAngel, ag.Max());
  250. //角度转弧度
  251. arcValue = robotRotateAngel * arc;
  252. }
  253. catch (Exception ex)
  254. {
  255. //LogHelper.WriteLog(ex.Message, ex);
  256. }
  257. return true;
  258. }
  259. private double arc1 = Math.PI / 180;
  260. /// <summary>
  261. /// 模板匹配
  262. /// </summary>
  263. /// <param name="tempalte">模板图片像</param>
  264. /// <param name="srcPic">被匹配图像</param>
  265. /// <returns>标注匹配区域的图像</returns>
  266. public Bitmap MatchTemplate(Mat tempalte, Mat srcPic, out OpenCvSharp.Point tempPoint, out double matchValue)
  267. {
  268. using (Mat result = new Mat()) //匹配结果
  269. { //模板匹配
  270. Double minVul;
  271. Double maxVul;
  272. OpenCvSharp.Point minLoc = new OpenCvSharp.Point(0, 0);
  273. OpenCvSharp.Point maxLoc = new OpenCvSharp.Point(0, 0);
  274. OpenCvSharp.Point matchLoc = new OpenCvSharp.Point(0, 0);
  275. Cv2.MatchTemplate(srcPic, tempalte, result, TemplateMatchModes.CCoeffNormed);//CCoeffNormed 最好匹配为1,值越小匹配越差 xxxNormed的算法无需归一化
  276. //Cv2.Normalize(result, result, 0, 1, NormTypes.MinMax, -1);//归一化
  277. Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
  278. //Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc, null);//查找极值
  279. matchLoc = maxLoc;//最大值坐标
  280. //result.Set(matchLoc.Y, matchLoc.X, 0);//改变最大值为最小值
  281. Mat mask = srcPic.Clone();//复制整个矩阵
  282. //Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
  283. matchValue = maxVul;
  284. tempPoint.X = matchLoc.X;
  285. tempPoint.Y = matchLoc.Y;
  286. //画框显示 :对角线画框,起点和终点,都用Point,线宽
  287. if (matchValue > TemplateMatchThreshold)
  288. {
  289. Cv2.Rectangle(mask, matchLoc, new OpenCvSharp.Point(matchLoc.X + tempalte.Cols, matchLoc.Y + tempalte.Rows), Scalar.Green, 2);//2代表画的线条的宽细程度
  290. }
  291. return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mask);
  292. }
  293. }
  294. /// <summary>
  295. /// 计算角度
  296. /// </summary>
  297. /// <param name="srcAngel"></param>
  298. /// <param name="rotateAngle"></param>
  299. /// <returns></returns>
  300. public double CalcRotateAngel(double srcAngel, int rotateAngle)
  301. {
  302. //逆时针旋转为负 顺时针旋转为正
  303. double tempSrcAngel = Math.Abs(srcAngel);
  304. double resultAngel = -1;
  305. if (tempSrcAngel >= 45)
  306. {
  307. switch (rotateAngle)
  308. {
  309. case 0:
  310. resultAngel = 90 - tempSrcAngel;
  311. break;
  312. case 90:
  313. resultAngel = 180 - tempSrcAngel;
  314. break;
  315. case 180:
  316. //逆时针
  317. resultAngel = -(90 + tempSrcAngel);
  318. break;
  319. case 270:
  320. //逆时针
  321. resultAngel = -tempSrcAngel;
  322. break;
  323. }
  324. }
  325. else if (tempSrcAngel < 45)
  326. {
  327. switch (rotateAngle)
  328. {
  329. case 0:
  330. //逆时针
  331. resultAngel = -tempSrcAngel;
  332. break;
  333. case 90:
  334. resultAngel = 90 - tempSrcAngel;
  335. break;
  336. case 180:
  337. resultAngel = 90 + tempSrcAngel;
  338. break;
  339. case 270:
  340. //逆时针
  341. resultAngel = -(90 + tempSrcAngel);
  342. break;
  343. }
  344. }
  345. return resultAngel;
  346. }

完整代码都在上面了,感兴趣的小伙伴可以在优化优化。下个案例见。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/663468
推荐阅读
相关标签
  

闽ICP备14008679号