BoundRectangleClass.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. using MvvmScaffoldFrame48.Model.StorageModel.ImageAlgorithm;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.Linq;
  6. namespace MvvmScaffoldFrame48.DLL.ImageAlgorithm
  7. {
  8. public class BoundRectangleClass
  9. {
  10. /// <summary>
  11. /// 凸包点集合获取
  12. /// </summary>
  13. /// <param name="points">点集合</param>
  14. /// <returns>凸包点集合</returns>
  15. public List<Point> ConvexHull(List<Point> points)
  16. {
  17. points = points.OrderBy(o => o.X).ThenBy(o => o.Y).ToList();
  18. var lower = new List<Point>();
  19. foreach (var p in points)
  20. {
  21. while (lower.Count >= 2 && Cross(lower[lower.Count - 2], lower[lower.Count - 1], p) <= 0)
  22. lower.RemoveAt(lower.Count - 1);
  23. lower.Add(p);
  24. }
  25. var upper = new List<Point>();
  26. for (int i = points.Count - 1; i >= 0; i--)
  27. {
  28. var p = points[i];
  29. while (upper.Count >= 2 && Cross(upper[upper.Count - 2], upper[upper.Count - 1], p) <= 0)
  30. upper.RemoveAt(upper.Count - 1);
  31. upper.Add(p);
  32. }
  33. lower.RemoveAt(lower.Count - 1);
  34. upper.RemoveAt(upper.Count - 1);
  35. lower.AddRange(upper);
  36. return lower;
  37. }
  38. /// <summary>
  39. /// 凸包最长长度
  40. /// </summary>
  41. /// <param name="hull"></param>
  42. /// <returns></returns>
  43. public BoundingRectangleMdoel RotatingCalipers(List<Point> hull)
  44. {
  45. BoundingRectangleMdoel result = null;
  46. int n = hull.Count;
  47. if (n == 1)
  48. {
  49. return result;
  50. }
  51. if (n == 2)
  52. {
  53. result = new BoundingRectangleMdoel
  54. {
  55. points = hull,
  56. CenterPoint = new Point() { X = (hull[1].X + hull[0].X)/2, Y = (hull[1].Y + hull[0].Y)/2 },
  57. Width = 0,
  58. Height = Distance(hull[0], hull[1]),
  59. Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X)
  60. };
  61. return result;
  62. }
  63. double maxDist = 0;
  64. int pointIndex1 = 0;
  65. int pointIndex2 = 0;
  66. for (int i = 0; i < n; i++)
  67. {
  68. for (int j = i + 1; j < n; j++)
  69. {
  70. if (Distance(hull[i], hull[j]) > maxDist)
  71. {
  72. maxDist = Distance(hull[i], hull[j]);
  73. pointIndex1 = i;
  74. pointIndex2 = j;
  75. }
  76. }
  77. }
  78. double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
  79. var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
  80. // 找到包围盒
  81. int minX = rotatedPoints.Min(p => p.X);
  82. int maxX = rotatedPoints.Max(p => p.X);
  83. int minY = rotatedPoints.Min(p => p.Y);
  84. int maxY = rotatedPoints.Max(p => p.Y);
  85. int centerX = (minX + maxX) / 2;
  86. int centerY = (minY + maxY) / 2;
  87. int width = maxX - minX;
  88. int height = maxY - minY;
  89. // 构造矩形的四个顶点并旋转回原来的角度
  90. var rectangle = new List<Point>
  91. {
  92. RotatePoint(new Point(minX, minY), angle),
  93. RotatePoint(new Point(maxX, minY), angle),
  94. RotatePoint(new Point(maxX, maxY), angle),
  95. RotatePoint(new Point(minX, maxY), angle)
  96. };
  97. result = new BoundingRectangleMdoel
  98. {
  99. points = rectangle,
  100. CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
  101. Width = width,
  102. Height = height,
  103. Angle = angle
  104. };
  105. return result;
  106. }
  107. /// <summary>
  108. /// 计算凸包的最小外接矩形
  109. /// </summary>
  110. /// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
  111. /// <returns>最小外接矩形的四个顶点</returns>
  112. public List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
  113. {
  114. if (convexHull == null || convexHull.Count < 3)
  115. return null;
  116. double minArea = double.MaxValue;
  117. List<Point> minRectangle = null;
  118. int n = convexHull.Count;
  119. // 遍历每一条边作为基准边
  120. for (int i = 0; i < n; i++)
  121. {
  122. Point edgeStart = convexHull[i];
  123. Point edgeEnd = convexHull[(i + 1) % n];
  124. // 计算当前边的角度
  125. double angle = Math.Atan2(edgeEnd.Y - edgeStart.Y, edgeEnd.X - edgeStart.X);
  126. // 将所有点绕原点旋转-angle角度,使当前边与x轴平行
  127. var rotatedPoints = convexHull.Select(p => RotatePoint(p, -angle)).ToList();
  128. // 找到包围盒
  129. int minX = rotatedPoints.Min(p => p.X);
  130. int maxX = rotatedPoints.Max(p => p.X);
  131. int minY = rotatedPoints.Min(p => p.Y);
  132. int maxY = rotatedPoints.Max(p => p.Y);
  133. // 计算面积
  134. double area = (maxX - minX) * (maxY - minY);
  135. // 如果面积更小,则更新最小矩形
  136. if (area < minArea)
  137. {
  138. minArea = area;
  139. // 构造矩形的四个顶点并旋转回原来的角度
  140. var rectangle = new List<Point>
  141. {
  142. RotatePoint(new Point(minX, minY), angle),
  143. RotatePoint(new Point(maxX, minY), angle),
  144. RotatePoint(new Point(maxX, maxY), angle),
  145. RotatePoint(new Point(minX, maxY), angle)
  146. };
  147. minRectangle = rectangle;
  148. }
  149. }
  150. return minRectangle;
  151. }
  152. /// <summary>
  153. /// 绕原点旋转点
  154. /// </summary>
  155. /// <param name="point">待旋转的点</param>
  156. /// <param name="angle">旋转角度(弧度)</param>
  157. /// <returns>旋转后的点</returns>
  158. private static Point RotatePoint(Point point, double angle)
  159. {
  160. double cos = Math.Cos(angle);
  161. double sin = Math.Sin(angle);
  162. return new Point(
  163. (int)(point.X * cos - point.Y * sin),
  164. (int)(point.X * sin + point.Y * cos)
  165. );
  166. }
  167. /// <summary>
  168. /// 计算点到线段的距离
  169. /// </summary>
  170. /// <param name="a">线段点1</param>
  171. /// <param name="b">线段点2</param>
  172. /// <param name="c">点三</param>
  173. /// <returns></returns>
  174. private double DistanceToLine(Point a, Point b, Point c)
  175. {
  176. double area = Math.Abs(Area2(a, b, c));
  177. double baseLength = Distance(a, b);
  178. return area / baseLength;
  179. }
  180. /// <summary>
  181. /// 计算向量叉积
  182. /// </summary>
  183. /// <param name="o"></param>
  184. /// <param name="a"></param>
  185. /// <param name="b"></param>
  186. /// <returns></returns>
  187. private int Cross(Point o, Point a, Point b) =>
  188. (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X);
  189. /// <summary>
  190. /// 计算三角形面积的两倍
  191. /// </summary>
  192. /// <param name="a"></param>
  193. /// <param name="b"></param>
  194. /// <param name="c"></param>
  195. /// <returns></returns>
  196. private int Area2(Point a, Point b, Point c) =>
  197. (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
  198. /// <summary>
  199. /// 计算两点间距离
  200. /// </summary>
  201. /// <param name="a"></param>
  202. /// <param name="b"></param>
  203. /// <returns></returns>
  204. private double Distance(Point a, Point b)
  205. {
  206. int dx = a.X - b.X;
  207. int dy = a.Y - b.Y;
  208. return Math.Sqrt(dx * dx + dy * dy);
  209. }
  210. }
  211. }