using o0; using o0.Num; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UnityEngine; using ZIM.Unity; using Color = UnityEngine.Color; namespace ZIM { // 亮区的点用来定位(计算center),泛光区域的点用来计算radius public class PixelSpotArea { //public static float gridRatio0 = 6f / 1280; //public static float gridRatio1 = 20f / 720; public static int gridLength0 = 2; // 亮区 public static int gridLength1 = 10; // 边缘泛光 public static (int x, int y) GetGrid0(Vector2 v) { var m = (int)(v.x / gridLength0); var n = (int)(v.y / gridLength0); return (m, n); } public static (int x, int y) GetGrid1(Vector2 v) { var m = (int)(v.x / gridLength1); var n = (int)(v.y / gridLength1); return (m, n); } // 亮点聚类到区域 public static List Cluster(List spotPoint, List brightPoint, Func PointFilter = null) { var spotArea = new List(); for (int i = 0; i < spotPoint.Count; i++) { var p = spotPoint[i]; if (PointFilter != null && !PointFilter(p)) // 筛选是否在屏幕内 continue; var grid = PixelSpotArea.GetGrid0(p); var join = new SortedList(); for (int j = 0; j < spotArea.Count; j++) { var area = spotArea[j]; if (area.Include0(grid)) join.Add(j, area); } if (join.Count == 0) spotArea.Add(new PixelSpotArea(p, grid, PixelSpotArea.GetGrid1(p))); else if (join.Count == 1) join.First().Value.Join(p, grid, PixelSpotArea.GetGrid1(p)); else { var combine = new PixelSpotArea(join.Values); combine.Join(p, grid, PixelSpotArea.GetGrid1(p)); spotArea.Add(combine); for (int j = join.Count - 1; j >= 0; j--) spotArea.RemoveAt(join.ElementAt(j).Key); } } if (spotArea.Count != 0) { // brightPoint合并到区域中 for (int i = 0; i < brightPoint.Count; i++) { var p = brightPoint[i]; var grid = PixelSpotArea.GetGrid1(p); for (int j = 0; j < spotArea.Count; j++) { var area = spotArea[j]; if (area.Include1(grid)) area.Add(p); } } } return spotArea; } Vector2 centroid = default; // 该项目里center不可能等于0,0 public Vector2 Centroid { get { if (centroid != default) return centroid; foreach (var p in Pixels0) { centroid += p; } return centroid /= Pixels0.Count; } } float radius = 0; public float Radius { set { radius = value; } get { if (radius != 0) return radius; //var radiusDic = new Dictionary() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } }; var radiusDic = new float[8]; foreach (var p in Pixels0) { var dir = p - Centroid; var radius = dir.magnitude; var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45); if (degreeI > 7) degreeI = 0; // 防止正好360度 if (radius > radiusDic[degreeI]) radiusDic[degreeI] = radius; } foreach (var p in Pixels1) { var dir = p - Centroid; var radius = dir.magnitude; var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45); if (degreeI > 7) degreeI = 0; if (radius > radiusDic[degreeI]) radiusDic[degreeI] = radius; } return radius = radiusDic.Mean(); } } public List Pixels0; public List Pixels1; HashSet<(int, int)> grids0; HashSet<(int, int)> grids1; public PixelSpotArea(Vector2 location, (int, int) grid0, (int, int) grid1) { Pixels0 = new List(1000); // 预估的初始容量 Pixels1 = new List(5000); this.grids0 = new HashSet<(int, int)>(); // hashset如果设置了太大的容量会降低效率 this.grids1 = new HashSet<(int, int)>(); Join(location, grid0, grid1); } public PixelSpotArea(IList areas) { Pixels0 = new List(1000); Pixels1 = new List(5000); grids0 = new HashSet<(int, int)>(); grids1 = new HashSet<(int, int)>(); foreach (var a in areas) { Pixels0.AddRange(a.Pixels0); grids0.AddRange(a.grids0); grids1.AddRange(a.grids1); //Center += a.Center * a.SpotPointCount; //SpotPointCount += a.SpotPointCount; } //Center /= SpotPointCount; } public bool Include0((int, int) grid) => grids0.Contains(grid); public bool Include1((int, int) grid) => grids1.Contains(grid); public void Join(Vector2 point) => Join(point, GetGrid0(point), GetGrid1(point)); public void Join(Vector2 point, (int x, int y) grid0, (int x, int y) grid1) { Pixels0.Add(point); //SpotPointCount++; //Center += (point - Center) / SpotPointCount; grids0.AddRange(new (int, int)[] { (grid0.x + -1, grid0.y + 0) , (grid0.x + 1 , grid0.y + 0), (grid0.x + 1, grid0.y + -1) , (grid0.x + -1, grid0.y + 1), (grid0.x + 0, grid0.y + -1) , (grid0.x + 0 , grid0.y + 1), (grid0.x + -1, grid0.y + -1) , (grid0.x + 1 , grid0.y + 1), grid0 }); grids1.AddRange(new (int, int)[] { (grid1.x + -1, grid1.y + 0) , (grid1.x + 1 , grid1.y + 0), (grid1.x + 1, grid1.y + -1) , (grid1.x + -1, grid1.y + 1), (grid1.x + 0, grid1.y + -1) , (grid1.x + 0 , grid1.y + 1), (grid1.x + -1, grid1.y + -1) , (grid1.x + 1 , grid1.y + 1), grid1 }); } // 不再join之后,才可以用add添加周围泛光 public void Add(Vector2 point) { //var radius = (point - Center).LengthManhattan(); //if (radius > MaxRadius) // MaxRadius = radius; Pixels1.Add(point); //var radius = (point - Center).LengthManhattan(); //if (radius > MaxRadius) // MaxRadius = radius; } public float TotalBrightness(Color[] pixels, Func Vector2ToIndex, int outer_size = 3) { (int x, int y) rectMin = ((int)(Centroid.x - Radius - outer_size), (int)(Centroid.y - Radius - outer_size)); (int x, int y) rectMax = ((int)(Centroid.x + Radius + outer_size), (int)(Centroid.y + Radius + outer_size)); var total = 0f; Parallel.For(rectMin.x, rectMax.x, (i) => { var t = 0; for (int j = rectMin.y; j < rectMax.y; j++) { var index = Vector2ToIndex(i, j); var b = pixels[index].Brightness(64); t += b; } lock (InfraredLocate.locker) { total += t; } }); //total /= (rectMax.y - rectMin.y) * (rectMax.x - rectMin.x); return total; } } }