PixelSpotArea.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using o0;
  2. using o0.Num;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using UnityEngine;
  8. using ZIM.Unity;
  9. using Color = UnityEngine.Color;
  10. namespace ZIM
  11. {
  12. // 亮区的点用来定位(计算center),泛光区域的点用来计算radius
  13. // 用格子进行聚类,格子的尺寸用函数UpdateGridLength更新
  14. public class PixelSpotArea: ISpotArea
  15. {
  16. public static int gridLength0 = 2; // 亮区
  17. public static int gridLength1 = 8; // 边缘泛光
  18. public static void UpdateGridLength(int sampling) // 根据采样密度更新格子的尺寸
  19. {
  20. gridLength0 = sampling * 2;
  21. gridLength1 = sampling * 8;
  22. }
  23. public static (int x, int y) GetGrid0(Vector2 v)
  24. {
  25. var m = (int)(v.x / gridLength0);
  26. var n = (int)(v.y / gridLength0);
  27. return (m, n);
  28. }
  29. public static (int x, int y) GetGrid1(Vector2 v)
  30. {
  31. var m = (int)(v.x / gridLength1);
  32. var n = (int)(v.y / gridLength1);
  33. return (m, n);
  34. }
  35. // 聚类算法
  36. public static List<ISpotArea> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, Func<Vector2, bool> PointFilter = null)
  37. {
  38. var spotArea = new List<PixelSpotArea>();
  39. for (int i = 0; i < spotPoint.Count; i++)
  40. {
  41. var p = spotPoint[i];
  42. if (PointFilter != null && !PointFilter(p)) // 筛选是否在屏幕内
  43. continue;
  44. var grid = PixelSpotArea.GetGrid0(p);
  45. var join = new SortedList<int, PixelSpotArea>();
  46. for (int j = 0; j < spotArea.Count; j++)
  47. {
  48. var area = spotArea[j];
  49. if (area.Include0(grid))
  50. join.Add(j, area);
  51. }
  52. if (join.Count == 0)
  53. spotArea.Add(new PixelSpotArea(p, grid, PixelSpotArea.GetGrid1(p)));
  54. else if (join.Count == 1)
  55. join.First().Value.Join(p, grid, PixelSpotArea.GetGrid1(p));
  56. else
  57. {
  58. var combine = new PixelSpotArea(join.Values);
  59. combine.Join(p, grid, PixelSpotArea.GetGrid1(p));
  60. spotArea.Add(combine);
  61. for (int j = join.Count - 1; j >= 0; j--)
  62. spotArea.RemoveAt(join.ElementAt(j).Key);
  63. }
  64. }
  65. // 至此亮区添加完,接下来添加泛光区
  66. if (spotArea.Count != 0)
  67. {
  68. // brightPoint合并到区域中
  69. // 先用亮区计算一遍半径
  70. foreach (var a in spotArea)
  71. a.CalculateRadius();
  72. for (int i = 0; i < brightPoint.Count; i++)
  73. {
  74. var p = brightPoint[i];
  75. var grid = PixelSpotArea.GetGrid1(p);
  76. var spotDistance = new float[spotArea.Count];
  77. for (int j = 0; j < spotArea.Count; j++)
  78. {
  79. var area = spotArea[j];
  80. if (area.Include1(grid))
  81. spotDistance[j] = (p - area.Centroid).magnitude - area.Radius;
  82. else
  83. spotDistance[j] = int.MaxValue;
  84. }
  85. var near = spotDistance.MinIndex();
  86. if (spotDistance[near] != int.MaxValue)
  87. spotArea[near].Pixels1.Add(p);
  88. }
  89. }
  90. // 添加亮区后计算一遍半径
  91. foreach (var a in spotArea)
  92. a.CalculateRadius();
  93. return spotArea.Select(i => i as ISpotArea).ToList();
  94. }
  95. Vector2 centroid = default; // 该项目里center不可能等于0,0
  96. public Vector2 Centroid
  97. {
  98. set { centroid = value; }
  99. // 在添加完亮区之后可以调用
  100. get
  101. {
  102. if (centroid != default)
  103. return centroid;
  104. return CalculateCentroid();
  105. }
  106. }
  107. float radius = 0;
  108. public float Radius
  109. {
  110. set { radius = value; }
  111. get
  112. {
  113. if (radius != 0)
  114. return radius;
  115. return CalculateRadius();
  116. }
  117. }
  118. public Vector2 CalculateCentroid()
  119. {
  120. centroid = Vector2.zero;
  121. foreach (var p in Pixels0)
  122. {
  123. centroid += p;
  124. }
  125. return centroid /= Pixels0.Count;
  126. }
  127. public float CalculateRadius()
  128. {
  129. //var radiusDic = new Dictionary<int, float>() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } };
  130. var radiusDic = new float[8];
  131. foreach (var p in Pixels0)
  132. {
  133. var dir = p - Centroid;
  134. var radius = dir.magnitude;
  135. var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
  136. if (degreeI > 7) degreeI = 0; // 防止正好360度
  137. if (radius > radiusDic[degreeI])
  138. radiusDic[degreeI] = radius;
  139. }
  140. foreach (var p in Pixels1)
  141. {
  142. var dir = p - Centroid;
  143. var radius = dir.magnitude;
  144. var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
  145. if (degreeI > 7) degreeI = 0;
  146. if (radius > radiusDic[degreeI])
  147. radiusDic[degreeI] = radius;
  148. }
  149. return radius = radiusDic.Mean();
  150. }
  151. public List<Vector2> Pixels0 { get; set; }
  152. public List<Vector2> Pixels1 { get; set; }
  153. HashSet<(int, int)> grids0;
  154. HashSet<(int, int)> grids1;
  155. public PixelSpotArea(Vector2 location, (int, int) grid0, (int, int) grid1)
  156. {
  157. Pixels0 = new List<Vector2>(50); // 预估的初始容量
  158. Pixels1 = new List<Vector2>(100);
  159. this.grids0 = new HashSet<(int, int)>(); // hashset如果设置了太大的容量会降低效率
  160. this.grids1 = new HashSet<(int, int)>();
  161. Join(location, grid0, grid1);
  162. }
  163. public PixelSpotArea(IList<PixelSpotArea> areas)
  164. {
  165. Pixels0 = new List<Vector2>(50);
  166. Pixels1 = new List<Vector2>(100);
  167. grids0 = new HashSet<(int, int)>();
  168. grids1 = new HashSet<(int, int)>();
  169. foreach (var a in areas)
  170. {
  171. Pixels0.AddRange(a.Pixels0);
  172. grids0.AddRange(a.grids0);
  173. grids1.AddRange(a.grids1);
  174. //Center += a.Center * a.SpotPointCount;
  175. //SpotPointCount += a.SpotPointCount;
  176. }
  177. //Center /= SpotPointCount;
  178. }
  179. public bool Include0((int, int) grid) => grids0.Contains(grid);
  180. public bool Include1((int, int) grid) => grids1.Contains(grid);
  181. public void Join(Vector2 point) => Join(point, GetGrid0(point), GetGrid1(point));
  182. public void Join(Vector2 point, (int x, int y) grid0, (int x, int y) grid1)
  183. {
  184. Pixels0.Add(point);
  185. //SpotPointCount++;
  186. //Center += (point - Center) / SpotPointCount;
  187. grids0.AddRange(new (int, int)[]
  188. {
  189. (grid0.x + -1, grid0.y + 0) , (grid0.x + 1 , grid0.y + 0),
  190. (grid0.x + 1, grid0.y + -1) , (grid0.x + -1, grid0.y + 1),
  191. (grid0.x + 0, grid0.y + -1) , (grid0.x + 0 , grid0.y + 1),
  192. (grid0.x + -1, grid0.y + -1) , (grid0.x + 1 , grid0.y + 1),
  193. grid0
  194. });
  195. grids1.AddRange(new (int, int)[]
  196. {
  197. (grid1.x + -1, grid1.y + 0) , (grid1.x + 1 , grid1.y + 0),
  198. (grid1.x + 1, grid1.y + -1) , (grid1.x + -1, grid1.y + 1),
  199. (grid1.x + 0, grid1.y + -1) , (grid1.x + 0 , grid1.y + 1),
  200. (grid1.x + -1, grid1.y + -1) , (grid1.x + 1 , grid1.y + 1),
  201. grid1
  202. });
  203. }
  204. // 不再join之后,才可以用add添加周围泛光
  205. //public void Add(Vector2 point)
  206. //{
  207. // Pixels1.Add(point);
  208. //}
  209. public float TotalBrightness(Color[] pixels, Func<int, int, int> Vector2ToIndex, int outer_size = 3)
  210. {
  211. (int x, int y) rectMin = ((int)(Centroid.x - Radius - outer_size), (int)(Centroid.y - Radius - outer_size));
  212. (int x, int y) rectMax = ((int)(Centroid.x + Radius + outer_size), (int)(Centroid.y + Radius + outer_size));
  213. var total = 0f;
  214. Parallel.For(rectMin.x, rectMax.x, (i) =>
  215. {
  216. var t = 0;
  217. for (int j = rectMin.y; j < rectMax.y; j++)
  218. {
  219. var index = Vector2ToIndex(i, j);
  220. var b = pixels[index].Brightness(64);
  221. t += b;
  222. }
  223. lock (InfraredLocate.locker)
  224. {
  225. total += t;
  226. }
  227. });
  228. //total /= (rectMax.y - rectMin.y) * (rectMax.x - rectMin.x);
  229. return total;
  230. }
  231. }
  232. }