PixelSpotArea.cs 8.3 KB

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