Просмотр исходного кода

聚类算法改成dbscan,并且会自动调整sampling

ZIM 1 год назад
Родитель
Сommit
da6978c179

+ 40 - 28
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredLocate.cs

@@ -42,7 +42,18 @@ namespace ZIM
         }
 
         readonly float[] spotBrightness = new float[] { 0.93f, 0.5f };      // 亮点阈值
-        int samplingScale = 1;
+        int _samplingScale = 1;
+        public int SamplingScale
+        {
+            get => _samplingScale;
+            set
+            {
+                if (value <= 0)
+                    throw new InvalidOperationException();
+                _samplingScale = value;
+                PixelSpotArea.UpdateGridLength(value);
+            }
+        }
         //const float circleVariance = 30f;
         //const int brightAreaRadius = 30; 
         //const int LeastBrightPoint = 10000;
@@ -120,8 +131,8 @@ namespace ZIM
             //watch.Start();
             //var times = new List<double>() { 0.0 };
 
-            (int x, int y) rectMin = ((int)rect.min.x / samplingScale, (int)rect.min.y / samplingScale);
-            (int x, int y) rectMax = ((int)rect.max.x / samplingScale, (int)rect.max.y / samplingScale);
+            (int x, int y) rectMin = ((int)rect.min.x / SamplingScale, (int)rect.min.y / SamplingScale);
+            (int x, int y) rectMax = ((int)rect.max.x / SamplingScale, (int)rect.max.y / SamplingScale);
 
             var spotPoint = new List<Vector2>(200);     // 预估的初始容量
             var brightPoint = new List<Vector2>(1000);
@@ -132,8 +143,8 @@ namespace ZIM
                 var points1 = new List<Vector2>(rectMax.y - rectMin.y);
                 for (int j = rectMin.y; j < rectMax.y; j++)
                 {
-                    int ip = i * samplingScale;
-                    int jp = j * samplingScale;
+                    int ip = i * SamplingScale;
+                    int jp = j * SamplingScale;
 
                     if (!screenIdentification.Screen.Active && ScreenPixelCheaker.OutArea2D(new Vector2(ip, jp), screenIdentification.Size))
                         continue;
@@ -160,18 +171,18 @@ namespace ZIM
             });
 
             //if (ScreenLocate.Main.DebugOnZIMDemo)
-            //{
-            //    if (spotPoint.Count > 400)     // 如果亮点太多,控制亮点数量在200左右
-            //    {
-            //        samplingScale = (int)Math.Ceiling(samplingScale * Math.Sqrt(spotPoint.Count / 400.0));
-            //        return new List<ISpotArea>();
-            //    }
-            //    else if (samplingScale > 1 && spotPoint.Count < 100)
-            //    {
-            //        samplingScale = Math.Max((int)Math.Ceiling(samplingScale * Math.Sqrt(spotPoint.Count / 200.0)), 1);
-            //        return new List<ISpotArea>();
-            //    }
-            //}
+            {
+                if (spotPoint.Count > 500)     // 如果亮点太多,控制亮点数量在500左右
+                {
+                    SamplingScale = (int)Math.Ceiling(SamplingScale * Math.Sqrt(spotPoint.Count / 500.0));
+                    return new List<ISpotArea>();
+                }
+                else if (SamplingScale > 1 && spotPoint.Count < 100)
+                {
+                    SamplingScale = Math.Max((int)Math.Ceiling(SamplingScale * Math.Sqrt(spotPoint.Count / 200.0)), 1);
+                    return new List<ISpotArea>();
+                }
+            }
 
             //times.Add(watch.ElapsedMilliseconds);
             //UnityEngine.Debug.Log("time1: " + (times[times.Count - 1] - times[times.Count - 2]));
@@ -201,9 +212,9 @@ namespace ZIM
                 //times.Add(watch.ElapsedMilliseconds);
                 //UnityEngine.Debug.Log("time2: " + (times[times.Count - 1] - times[times.Count - 2]));
 
-                //var db = Dbscan.Run(spotPoint, ZIM.Unity.ZIMMath.LengthManhattan, samplingScale * 2, 3);
-                //var spotArea = DbscanToSpotAreas(db, brightPoint);
-                var spotArea = PixelSpotArea.Cluster(spotPoint, brightPoint, screenIdentification.Screen.UVInScreen);
+                var db = Dbscan.Run(spotPoint, ZIM.Unity.ZIMMath.LengthManhattan, SamplingScale, 4);
+                var spotArea = DbscanToSpotAreas(db, brightPoint);
+                //var spotArea = PixelSpotArea.Cluster(spotPoint, brightPoint, screenIdentification.Screen.UVInScreen);
 
                 if (ScreenLocate.Main.DebugOnZIMDemo)
                     DebugAreas(spotArea);
@@ -331,8 +342,8 @@ namespace ZIM
             // 将泛光点添加到最近的area
             foreach (var i in brightPoint)
             {
-                var index = FindNearestAreaIndex(i, areaIncludeRadius);
-                if (index < clusters.Count)
+                var index = FindNearestAreaIndex(i, areaIncludeRadius,out float nearestDistance);
+                if (index >= 0 && index < clusters.Count && nearestDistance < PixelSpotArea.gridLength1) 
                     clusters[index].Pixels1.Add(i);
             }
             // 添加了泛光点后,重新计算半径
@@ -342,13 +353,14 @@ namespace ZIM
             return clusters;
         }
 
-        private int FindNearestAreaIndex(Vector2 a, List<(Vector2, float)> areas)
+        private int FindNearestAreaIndex(Vector2 a, List<(Vector2, float)> areas, out float nearestDistance)
         {
+            nearestDistance = float.MaxValue;
             if (areas == null || areas.Count == 0)
                 return -1;
 
             int nearestIndex = 0;
-            float nearestDistance = Vector2.Distance(a, areas[0].Item1) - areas[0].Item2;
+            nearestDistance = Vector2.Distance(a, areas[0].Item1) - areas[0].Item2;
 
             for (int i = 1; i < areas.Count; i++)
             {
@@ -620,12 +632,12 @@ namespace ZIM
         {
             var brightPixelDic = new Dictionary<float, List<Vector2>>();
             (int x, int y) origin = ((int)rect.min.x + 1, (int)rect.min.y + 1);
-            Parallel.For(origin.x / samplingScale, (origin.x + (int)rect.width) / samplingScale, (i) =>
+            Parallel.For(origin.x / SamplingScale, (origin.x + (int)rect.width) / SamplingScale, (i) =>
             {
-                for (int j = origin.y / samplingScale; j < (origin.y + rect.height) / samplingScale; j++)
+                for (int j = origin.y / SamplingScale; j < (origin.y + rect.height) / SamplingScale; j++)
                 {
-                    int ip = i * samplingScale;
-                    int jp = j * samplingScale;
+                    int ip = i * SamplingScale;
+                    int jp = j * SamplingScale;
                     var index = mCameraInfo.CoordToIndex(ip, jp);
                     //var b = brightness[index];
                     //var b = (int)Math.Round(ConvBrightness(brightness, (ip, jp)));

+ 30 - 12
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelSpotArea.cs

@@ -11,13 +11,16 @@ using Color = UnityEngine.Color;
 namespace ZIM
 {
     // 亮区的点用来定位(计算center),泛光区域的点用来计算radius
+    // 用格子进行聚类,格子的尺寸用函数UpdateGridLength更新
     public class PixelSpotArea: ISpotArea
     {
-        //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 gridLength0 = 2;     // 亮区
+        public static int gridLength1 = 8;     // 边缘泛光
+        public static void UpdateGridLength(int sampling)       // 根据采样密度更新格子的尺寸
+        {
+            gridLength0 = sampling * 2;
+            gridLength1 = sampling * 8;
+        }
 
         public static (int x, int y) GetGrid0(Vector2 v)
         {
@@ -33,7 +36,7 @@ namespace ZIM
             return (m, n);
         }
 
-        // 亮点聚类到区域
+        // 聚类算法
         public static List<ISpotArea> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, Func<Vector2, bool> PointFilter = null)
         {
             var spotArea = new List<PixelSpotArea>();
@@ -64,29 +67,44 @@ namespace ZIM
                         spotArea.RemoveAt(join.ElementAt(j).Key);
                 }
             }
-
+            // 至此亮区添加完,接下来添加泛光区
             if (spotArea.Count != 0)
             {
                 // brightPoint合并到区域中
+                // 先用亮区计算一遍半径
+                foreach (var a in spotArea)
+                    a.CalculateRadius();
+
                 for (int i = 0; i < brightPoint.Count; i++)
                 {
                     var p = brightPoint[i];
                     var grid = PixelSpotArea.GetGrid1(p);
+                    var spotDistance = new float[spotArea.Count];
                     for (int j = 0; j < spotArea.Count; j++)
                     {
                         var area = spotArea[j];
                         if (area.Include1(grid))
-                            area.Pixels1.Add(p);
+                            spotDistance[j] = (p - area.Centroid).magnitude - area.Radius;
+                        else
+                            spotDistance[j] = int.MaxValue;
                     }
+                    var near = spotDistance.MinIndex();
+                    if (spotDistance[near] != int.MaxValue)
+                        spotArea[near].Pixels1.Add(p);
                 }
             }
 
+            // 添加亮区后计算一遍半径
+            foreach (var a in spotArea)
+                a.CalculateRadius();
+
             return spotArea.Select(i => i as ISpotArea).ToList();
         }
 
         Vector2 centroid = default;       // 该项目里center不可能等于0,0
         public Vector2 Centroid
         {
+            // 在添加完亮区之后可以调用
             get
             {
                 if (centroid != default)
@@ -151,8 +169,8 @@ namespace ZIM
 
         public PixelSpotArea(Vector2 location, (int, int) grid0, (int, int) grid1)
         {
-            Pixels0 = new List<Vector2>(1000);       // 预估的初始容量
-            Pixels1 = new List<Vector2>(5000);
+            Pixels0 = new List<Vector2>(50);       // 预估的初始容量
+            Pixels1 = new List<Vector2>(100);
             this.grids0 = new HashSet<(int, int)>();        // hashset如果设置了太大的容量会降低效率
             this.grids1 = new HashSet<(int, int)>();
             Join(location, grid0, grid1);
@@ -160,8 +178,8 @@ namespace ZIM
 
         public PixelSpotArea(IList<PixelSpotArea> areas)
         {
-            Pixels0 = new List<Vector2>(1000);
-            Pixels1 = new List<Vector2>(5000);
+            Pixels0 = new List<Vector2>(50);
+            Pixels1 = new List<Vector2>(100);
             grids0 = new HashSet<(int, int)>();
             grids1 = new HashSet<(int, int)>();
             foreach (var a in areas)