当前位置:   article > 正文

OpenCv案例(九): 基于OpenCvSharp图像分割提取目标区域和定位

opencvsharp

以下原图中,物体连靠在一起,目的是将其分割开,再提取轮廓和定位

原图: 

 最终效果:

麻烦的地方是,分割开右下角部分,两个连在一起的目标物体,下图所示: 

基本方法:BoxFilter滤波、二值化、轮廓提取,凸包检测,图像的矩

代码如下:

  1. /// <summary>
  2. /// 获取分割点
  3. /// </summary>
  4. /// <param name="contours"></param>
  5. /// <param name="contourCount"></param>
  6. /// <param name="arcLength"></param>
  7. /// <param name="farDistance"></param>
  8. /// <returns></returns>
  9. public List<Point> GetSplitPoints(Point[][] contours, List<int> contourCount, int arcLength, int farDistance)
  10. {
  11. #region 凸包检测
  12. List<double> lArc = new List<double>();
  13. //Mat src = srcImage.Clone();
  14. List<Point[]> lpContours = new List<Point[]>();
  15. List<int> hulls = new List<int>();
  16. Point lastP = new Point();
  17. Point firstP = new Point();
  18. Point farLastP = new Point();
  19. List<Point> lps = new List<Point>();
  20. int dot = 1;
  21. List<int> depth = new List<int>();
  22. for (int i = 0; i < contourCount.Count; i++)
  23. {
  24. InputArray inputArray = InputArray.Create<Point>(contours[contourCount[i]]);
  25. OutputArray outputArray = OutputArray.Create(hulls);
  26. Cv2.ConvexHull(inputArray, outputArray, false, false);
  27. if (Cv2.ArcLength(inputArray, true) < arcLength)
  28. {
  29. //lArc.Add(Cv2.ArcLength(inputArray, true));
  30. continue;
  31. }
  32. //前三个值得含义分别为:凸缺陷的起始点,凸缺陷的终点,凸缺陷的最深点(即边缘点到凸包距离最大点)。
  33. var defects = Cv2.ConvexityDefects(contours[contourCount[i]], hulls);
  34. for (int j = 0; j < defects.Length; j++)
  35. {
  36. OpenCvSharp.Point start = contours[contourCount[i]][defects[j].Item0];
  37. OpenCvSharp.Point end = contours[contourCount[i]][defects[j].Item1];
  38. OpenCvSharp.Point far = contours[contourCount[i]][defects[j].Item2];
  39. //OpenCvSharp.Point fart = contours[contourCount[i]][defects[j].Item3];
  40. if (defects[j].Item3 > farDistance) //(4500 < defects[j].Item3 && defects[j].Item3 < 300000)
  41. {
  42. lps.Add(contours[contourCount[i]][defects[j].Item2]);
  43. depth.Add(defects[j].Item3);
  44. }
  45. }
  46. }
  47. #endregion
  48. return lps;
  49. }
  50. /// <summary>
  51. /// 获取最小内接矩形
  52. /// </summary>
  53. /// <param name="contours"></param>
  54. /// <param name="contourCount"></param>
  55. /// <returns></returns>
  56. public List<RotatedRect> GetMinRects(Point[][] contours, List<int> contourCount)
  57. {
  58. //Cv2.ImShow(",mmmm", morphImage);
  59. //double rotateAngel = 0;
  60. Point2f[] vertices = new Point2f[4];
  61. //Point2f minRectcenterPoint = new Point2f();
  62. List<RotatedRect> minRects = new List<RotatedRect>();
  63. for (int i = 0; i < contourCount.Count; i++)
  64. {
  65. //获取轮廓点的矩形区域
  66. //绘制Rio区域最小矩形
  67. #region 绘制Rio区域最小矩形
  68. RotatedRect minRect = Cv2.MinAreaRect(contours[contourCount[i]]);
  69. minRects.Add(minRect);
  70. #endregion
  71. }
  72. return minRects;
  73. }
  74. /// <summary>
  75. /// 返回设置范围内的轮廓
  76. /// </summary>
  77. /// <param name="mat"></param>
  78. /// <param name="range1"></param>
  79. /// <param name="range2"></param>
  80. /// <param name="contourCount"></param>
  81. /// <returns></returns>
  82. public Point[][] GetImageContours(Mat mat, int length, out List<int> contourCount)
  83. {
  84. List<double> arclength = new List<double>();
  85. OpenCvSharp.Point[][] contours;
  86. HierarchyIndex[] hierarchies;
  87. //Cv2.ImShow(",mmmm", mat);
  88. Cv2.FindContours(mat, out contours, out hierarchies, RetrievalModes.External, ContourApproximationModes.ApproxSimple, new Point());
  89. Mat connImg = Mat.Zeros(mat.Size(), MatType.CV_8UC3);
  90. Point2f[] vertices = new Point2f[4];
  91. Mat drawOutline = Mat.Zeros(mat.Size(), mat.Type());
  92. int sum = 0;
  93. contourCount = new List<int>();
  94. for (int i = 0; i < contours.Length; i++)
  95. {
  96. Rect rect1 = Cv2.BoundingRect(contours[i]);
  97. if (Cv2.ArcLength(contours[i], true) > length)//(rect1.Width > range1 && rect1.Height < range2)
  98. {
  99. Cv2.DrawContours(drawOutline, contours, i, new Scalar(255, 0, 255), 2, LineTypes.Link8, hierarchies);
  100. contourCount.Add(i);
  101. arclength.Add(Cv2.ArcLength(contours[i], true));
  102. sum++;
  103. }
  104. }
  105. Cv2.ImShow("contours", drawOutline);
  106. return contours;
  107. }
  108. /// <summary>
  109. /// 图像灰度
  110. /// 盒子滤波 保留边缘信息
  111. /// 自适应阈值 效果不错 无需形态学降噪
  112. /// 取反操作
  113. /// 过滤不需要轮廓信息(面积 边长)
  114. /// 轮廓提取
  115. /// (以上每一步都很重要,否则,无法获取良好的轮廓)
  116. /// 凸包检测
  117. /// 根据轮廓信息,查找大凸包,获取分割点
  118. /// 重新操作图像
  119. /// 在二值化图像时,分割连接点位置
  120. /// 绘制轮廓
  121. /// 绘制最小内接矩形和质心点
  122. /// 识别目标位置完成
  123. /// 注意:不同大小的图像处理时,需要修改自适应阈值参数、轮廓过滤面积、凸包检测的分割点过滤
  124. /// </summary>
  125. /// <param name="srcImage"></param>
  126. /// <returns></returns>
  127. public Mat PreProcess(Mat srcImage)
  128. {
  129. Mat grayMat = new Mat();
  130. Cv2.CvtColor(srcImage, grayMat, ColorConversionCodes.BGRA2GRAY);
  131. //Cv2.ImShow("grayMat", grayMat);
  132. Mat blurImg = BoxFilter(grayMat);
  133. //Cv2.ImShow("blurImg", blurImg);
  134. // 注意:不同大小的图像处理时,需要修改参数
  135. Mat threshold = new Mat();
  136. Cv2.AdaptiveThreshold(blurImg, threshold, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 15, 2);
  137. //Cv2.Threshold(threshold, threshold, 0, 255, ThresholdTypes.BinaryInv);
  138. Cv2.ImShow("threshold", threshold);
  139. //Mat morphImg = MorphImage(threshold, MorphShapes.Ellipse, MorphTypes.Dilate, 1, new OpenCvSharp.Size(3, 3));
  140. //Cv2.ImShow("morphImg", morphImg);
  141. //Mat cannyImg = new Mat();
  142. //Cv2.Laplacian(morphImg2, cannyImg, MatType.CV_8UC3, 5, 1);//Cv2.Canny(morphImg, cannyImg, 30, 90);//3和4参数的 最佳比例在1/3和1/2之间
  143. //Cv2.ImShow("cannyImg", cannyImg);
  144. Mat bitwiseMat = new Mat();
  145. Cv2.BitwiseNot(threshold, bitwiseMat);
  146. Cv2.ImShow("bitwiseMat", bitwiseMat);
  147. List<int> contourCount;
  148. //轮廓提取
  149. Point[][] contours = GetImageContours(bitwiseMat, 600, out contourCount);
  150. //凸包检测
  151. List<Point> lps = GetSplitPoints(contours, contourCount, 800, 4500);
  152. // 注意:不同大小的图像处理时,需要修改参数
  153. //重新处理
  154. Cv2.AdaptiveThreshold(blurImg, threshold, 255.0, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 13, 2);
  155. Cv2.ImShow("threshold1", threshold);
  156. //MorphImage(threshold, MorphShapes.Ellipse, MorphTypes.Close, 1, new OpenCvSharp.Size(3, 3));
  157. //Cv2.ImShow("morphImg1", morphImg);
  158. Cv2.BitwiseNot(threshold, bitwiseMat);
  159. Cv2.ImShow("bitwiseMat1", bitwiseMat);
  160. //提取凸显点坐标
  161. if (lps.Count > 1)
  162. {
  163. Cv2.Line(bitwiseMat, lps[0], lps[1], Scalar.Black, 2, LineTypes.Link8);
  164. }
  165. Cv2.ImShow("bitwiseMat2", bitwiseMat);
  166. //轮廓提取
  167. contourCount.Clear(); // 注意:不同大小的图像处理时,需要修改length参数
  168. Point[][] newContours = GetImageContours(bitwiseMat, 550, out contourCount);
  169. List<RotatedRect> rotatedRects = GetMinRects(newContours, contourCount);
  170. for (int i = 0; i < rotatedRects.Count; i++)
  171. {
  172. #region 绘制Rio区域最小矩形
  173. Point2f[] vertices = rotatedRects[i].Points();
  174. #endregion
  175. //绘制最小矩形
  176. #region 绘制最小矩形
  177. 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, 0, 255), 2);
  178. 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, 0, 255), 2);
  179. 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, 0, 255), 2);
  180. 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, 0, 255), 2);
  181. //获取重心点
  182. Moments M;
  183. M = Cv2.Moments(vertices);
  184. double cX = M.M10 / M.M00;
  185. double cY = M.M01 / M.M00;
  186. //显示目标中心并提取坐标点
  187. Cv2.Circle(srcImage, (int)cX, (int)cY, 2, Scalar.Yellow, 2);
  188. //Console.WriteLine("AngleRect_angle: {0}", minRect.Angle);
  189. #endregion
  190. }
  191. Cv2.ImShow("srcImage", srcImage);
  192. return null;
  193. }

灰度图像后图像二值化:

图像取反

 

 绘制轮廓

 凸包检测,查找分割点,下图黄色点标记处即找到的分割点位置

 将找到的分割点在二值化图像中,连接一条线后,重新轮廓识别即可分割

最小轮廓矩形提取和绘制,以及绘制质心位置

 到此,已将连接处分隔开

注意:使用以上方法是需要根据图像大小设置部分参数,例如二值化处理参数、过滤轮廓形状大小,凸包检测点的获取等位置,需要根据实际情况设置参数;

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

闽ICP备14008679号