Program.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace MinBoundRectangle
  9. {
  10. internal class Program
  11. {
  12. static void Main(string[] args)
  13. {
  14. BlobMaxRectangle blobMaxRectangle = new BlobMaxRectangle();
  15. List<RowStartEndCol> InitPints = new List<RowStartEndCol>()
  16. {
  17. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2201 , EndCol = 2201 },
  18. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2203 , EndCol = 2224 },
  19. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2226 , EndCol = 2226 },
  20. };
  21. blobMaxRectangle.ConvexHull(InitPints);
  22. List<RowStartEndCol> Rows = new List<RowStartEndCol>()
  23. {
  24. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2201 , EndCol = 2201 },
  25. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2203 , EndCol = 2224 },
  26. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2226 , EndCol = 2226 },
  27. new RowStartEndCol(){ RowsCol = 2811 ,StartCol = 2232 , EndCol = 2232 },
  28. new RowStartEndCol(){ RowsCol = 2812 ,StartCol = 2196 , EndCol = 2235 },
  29. new RowStartEndCol(){ RowsCol = 2813 ,StartCol = 2189 , EndCol = 2243 },
  30. new RowStartEndCol(){ RowsCol = 2813 ,StartCol = 2245 , EndCol = 2245 },
  31. new RowStartEndCol(){ RowsCol = 2814 ,StartCol = 2186 , EndCol = 2250 },
  32. new RowStartEndCol(){ RowsCol = 2815 ,StartCol = 2180 , EndCol = 2258 },
  33. new RowStartEndCol(){ RowsCol = 2816 ,StartCol = 2179 , EndCol = 2261 },
  34. new RowStartEndCol(){ RowsCol = 2817 ,StartCol = 2175 , EndCol = 2266 },
  35. new RowStartEndCol(){ RowsCol = 2818 ,StartCol = 2174 , EndCol = 2269 },
  36. new RowStartEndCol(){ RowsCol = 2819 ,StartCol = 2172 , EndCol = 2273 },
  37. new RowStartEndCol(){ RowsCol = 2820 ,StartCol = 2170 , EndCol = 2275 },
  38. new RowStartEndCol(){ RowsCol = 2821 ,StartCol = 2170 , EndCol = 2280 },
  39. new RowStartEndCol(){ RowsCol = 2822 ,StartCol = 2170 , EndCol = 2283 },
  40. new RowStartEndCol(){ RowsCol = 2823 ,StartCol = 2169 , EndCol = 2285 },
  41. new RowStartEndCol(){ RowsCol = 2824 ,StartCol = 2168 , EndCol = 2287 },
  42. new RowStartEndCol(){ RowsCol = 2825 ,StartCol = 2168 , EndCol = 2291 },
  43. new RowStartEndCol(){ RowsCol = 2826 ,StartCol = 2168 , EndCol = 2293 }
  44. };
  45. for(int i = 0; i < 10; i++)
  46. {
  47. Stopwatch stopwatch = Stopwatch.StartNew();
  48. var Conveexs = blobMaxRectangle.ConvexHull(Rows);
  49. var MaxLength = blobMaxRectangle.RotatingCalipers(Conveexs);
  50. var MinMRectangle = blobMaxRectangle.CalculateMinimumBoundingRectangle(Conveexs);
  51. stopwatch.Stop();
  52. Console.WriteLine($"额外计算耗时:{stopwatch.Elapsed}");
  53. Console.WriteLine(MaxLength);
  54. }
  55. }
  56. }
  57. // 活动物体跟踪类
  58. public class RowStartEndCol
  59. {
  60. /// <summary>
  61. /// 当前有效区行数
  62. /// </summary>
  63. public long RowsCol { get; set; }
  64. /// <summary>
  65. /// 当前行有效区域起始列坐标
  66. /// </summary>
  67. public int StartCol { get; set; }
  68. /// <summary>
  69. /// 当前行有效区域结束列坐标
  70. /// </summary>
  71. public int EndCol { get; set; }
  72. }
  73. public class BlobMaxRectangle
  74. {
  75. /// <summary>
  76. /// 凸包点集合获取
  77. /// </summary>
  78. /// <param name="Rows"></param>
  79. /// <returns></returns>
  80. public List<Point> ConvexHull(List<RowStartEndCol> Rows)
  81. {
  82. List<Point> points = Rows.Select(o => new Point(o.StartCol, (int)o.RowsCol)).ToList();
  83. points.AddRange(Rows.Select(o => new Point(o.EndCol, (int)o.RowsCol)).ToList());
  84. points = points.OrderBy(o => o.X).ThenBy(o => o.Y).ToList();
  85. var lower = new List<Point>();
  86. foreach (var p in points)
  87. {
  88. while (lower.Count >= 2 && Cross(lower[lower.Count - 2], lower[lower.Count - 1], p) <= 0)
  89. lower.RemoveAt(lower.Count - 1);
  90. lower.Add(p);
  91. }
  92. var upper = new List<Point>();
  93. for (int i = points.Count - 1; i >= 0; i--)
  94. {
  95. var p = points[i];
  96. while (upper.Count >= 2 && Cross(upper[upper.Count - 2], upper[upper.Count - 1], p) <= 0)
  97. upper.RemoveAt(upper.Count - 1);
  98. upper.Add(p);
  99. }
  100. lower.RemoveAt(lower.Count - 1);
  101. upper.RemoveAt(upper.Count - 1);
  102. lower.AddRange(upper);
  103. return lower;
  104. }
  105. /// <summary>
  106. /// 凸包最长长度
  107. /// </summary>
  108. /// <param name="hull"></param>
  109. /// <returns></returns>
  110. public double RotatingCalipers(List<Point> hull)
  111. {
  112. int n = hull.Count;
  113. if (n == 1) return 0;
  114. if (n == 2) return Distance(hull[0], hull[1]);
  115. double maxDist = 0;
  116. int pointIndex1 = 0;
  117. int pointIndex2 = 0;
  118. for (int i = 0; i < n; i++)
  119. {
  120. for (int j = i + 1; j < n; j++)
  121. {
  122. if(Distance(hull[i], hull[j]) > maxDist)
  123. {
  124. maxDist = Distance(hull[i], hull[j]);
  125. pointIndex1 = i;
  126. pointIndex2 = j;
  127. }
  128. }
  129. }
  130. /* 计算最长长度的结果的宽度
  131. double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
  132. var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
  133. // 找到包围盒
  134. int minX = rotatedPoints.Min(p => p.X);
  135. int maxX = rotatedPoints.Max(p => p.X);
  136. int minY = rotatedPoints.Min(p => p.Y);
  137. int maxY = rotatedPoints.Max(p => p.Y);
  138. int width = maxX - minX;
  139. int height = maxY - minY;
  140. // 构造矩形的四个顶点并旋转回原来的角度
  141. var rectangle = new List<Point>
  142. {
  143. RotatePoint(new Point(minX, minY), angle),
  144. RotatePoint(new Point(maxX, minY), angle),
  145. RotatePoint(new Point(maxX, maxY), angle),
  146. RotatePoint(new Point(minX, maxY), angle)
  147. };
  148. */
  149. return maxDist;
  150. }
  151. /// <summary>
  152. /// 计算凸包的最小外接矩形
  153. /// </summary>
  154. /// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
  155. /// <returns>最小外接矩形的四个顶点</returns>
  156. public List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
  157. {
  158. if (convexHull == null || convexHull.Count < 3)
  159. return null;
  160. double minArea = double.MaxValue;
  161. List<Point> minRectangle = null;
  162. int n = convexHull.Count;
  163. // 遍历每一条边作为基准边
  164. for (int i = 0; i < n; i++)
  165. {
  166. Point edgeStart = convexHull[i];
  167. Point edgeEnd = convexHull[(i + 1) % n];
  168. // 计算当前边的角度
  169. double angle = Math.Atan2(edgeEnd.Y - edgeStart.Y, edgeEnd.X - edgeStart.X);
  170. // 将所有点绕原点旋转-angle角度,使当前边与x轴平行
  171. var rotatedPoints = convexHull.Select(p => RotatePoint(p, -angle)).ToList();
  172. // 找到包围盒
  173. int minX = rotatedPoints.Min(p => p.X);
  174. int maxX = rotatedPoints.Max(p => p.X);
  175. int minY = rotatedPoints.Min(p => p.Y);
  176. int maxY = rotatedPoints.Max(p => p.Y);
  177. // 计算面积
  178. double area = (maxX - minX) * (maxY - minY);
  179. // 如果面积更小,则更新最小矩形
  180. if (area < minArea)
  181. {
  182. minArea = area;
  183. // 构造矩形的四个顶点并旋转回原来的角度
  184. var rectangle = new List<Point>
  185. {
  186. RotatePoint(new Point(minX, minY), angle),
  187. RotatePoint(new Point(maxX, minY), angle),
  188. RotatePoint(new Point(maxX, maxY), angle),
  189. RotatePoint(new Point(minX, maxY), angle)
  190. };
  191. minRectangle = rectangle;
  192. }
  193. }
  194. return minRectangle;
  195. }
  196. /// <summary>
  197. /// 绕原点旋转点
  198. /// </summary>
  199. /// <param name="point">待旋转的点</param>
  200. /// <param name="angle">旋转角度(弧度)</param>
  201. /// <returns>旋转后的点</returns>
  202. private static Point RotatePoint(Point point, double angle)
  203. {
  204. double cos = Math.Cos(angle);
  205. double sin = Math.Sin(angle);
  206. return new Point(
  207. (int)(point.X * cos - point.Y * sin),
  208. (int)(point.X * sin + point.Y * cos)
  209. );
  210. }
  211. /// <summary>
  212. /// 计算点到线段的距离
  213. /// </summary>
  214. /// <param name="a">线段点1</param>
  215. /// <param name="b">线段点2</param>
  216. /// <param name="c">点三</param>
  217. /// <returns></returns>
  218. private double DistanceToLine(Point a, Point b, Point c)
  219. {
  220. double area = Math.Abs(Area2(a, b, c));
  221. double baseLength = Distance(a, b);
  222. return area / baseLength;
  223. }
  224. /// <summary>
  225. /// 计算向量叉积
  226. /// </summary>
  227. /// <param name="o"></param>
  228. /// <param name="a"></param>
  229. /// <param name="b"></param>
  230. /// <returns></returns>
  231. private int Cross(Point o, Point a, Point b) =>
  232. (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X);
  233. /// <summary>
  234. /// 计算三角形面积的两倍
  235. /// </summary>
  236. /// <param name="a"></param>
  237. /// <param name="b"></param>
  238. /// <param name="c"></param>
  239. /// <returns></returns>
  240. private int Area2(Point a, Point b, Point c) =>
  241. (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
  242. /// <summary>
  243. /// 计算两点间距离
  244. /// </summary>
  245. /// <param name="a"></param>
  246. /// <param name="b"></param>
  247. /// <returns></returns>
  248. private double Distance(Point a, Point b)
  249. {
  250. int dx = a.X - b.X;
  251. int dy = a.Y - b.Y;
  252. return Math.Sqrt(dx * dx + dy * dy);
  253. }
  254. }
  255. }