| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- using o0;
- using o0.Num;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using UnityEngine;
- using static ScreenLocate;
- namespace ZIM
- {
- public class KMeans
- {
- public System.Random Rand = new System.Random();
- // 用轮廓系数评价K值
- public List<PixelSpotAreaOld> InfraredSpotCluster(List<Vector2> spotPoint, List<Vector2> brightPoint, int spotMaxCount, Func<Vector2, bool> PointFilter = null)
- {
- if (PointFilter != null) // 筛掉屏幕外的点
- {
- var temp0 = new List<Vector2>(spotPoint.Count);
- var temp1 = new List<Vector2>(spotPoint.Count);
- foreach (var p in spotPoint)
- {
- if (PointFilter(p))
- temp0.Add(p);
- }
- foreach (var p in brightPoint)
- {
- if (PointFilter(p))
- temp1.Add(p);
- }
- spotPoint = temp0;
- brightPoint = temp1;
- }
- // 从1-k,计算多次聚类,再评估选出best k
- List<PixelSpotAreaOld>[] ClustersArray = new List<PixelSpotAreaOld>[spotMaxCount]; // 各个K对应的Clusters
- float[] silhouetteScores = new float[spotMaxCount]; // 轮廓系数
- for (int i = 0; i < spotMaxCount; i++)
- {
- int k = i + 1;
- var clusters = Cluster(spotPoint, brightPoint, k);
- Debug.Log("k: " + k);
- foreach (var j in clusters)
- {
- Debug.Log(j.Centroid + ", " + j.Radius);
- }
- silhouetteScores[i] = CalculateSilhouetteScore(spotPoint, clusters);
- ClustersArray[i] = clusters;
- Debug.Log("silhouetteScore: " + silhouetteScores[i]);
- }
- int best = silhouetteScores.MaxIndex();
- foreach (var j in ClustersArray[best])
- {
- Debug.Log("select: " + j.Centroid + ", " + j.Radius);
- }
- return ClustersArray[best];
- }
- // 计算轮廓系数
- public float CalculateSilhouetteScore(List<Vector2> points, List<PixelSpotAreaOld> clusters)
- {
- int numPoints = points.Count;
- float totalSilhouetteScore = 0f;
- Dictionary<Vector2, int> pointToClusterMap = new Dictionary<Vector2, int>();
- for (int i = 0; i < clusters.Count; i++)
- {
- foreach (var p in clusters[i].Pixels0)
- pointToClusterMap[p] = i;
- }
- foreach (var point in points)
- {
- int clusterIndex = pointToClusterMap[point];
- float a = AverageDistance(point, clusters[clusterIndex]);
- float b = float.MaxValue;
- for (int i = 0; i < clusters.Count; i++)
- {
- if (i == clusterIndex) continue;
- float distance = AverageDistance(point, clusters[i]);
- if (distance < b)
- {
- b = distance;
- }
- }
- float s = (b - a) / Mathf.Max(a, b);
- totalSilhouetteScore += s;
- }
- return totalSilhouetteScore / numPoints;
- }
- private float AverageDistance(Vector2 point, PixelSpotAreaOld cluster)
- {
- float totalDistance = 0f;
- foreach (var c in cluster.Pixels0)
- totalDistance += Vector2.Distance(point, c);
- return totalDistance / cluster.Pixels0.Count;
- }
- // KMeans聚类
- private List<PixelSpotAreaOld> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, int k, int maxIterations = 10)
- {
- if (spotPoint.Count < k)
- return new List<PixelSpotAreaOld>();
- var clusters = InitializeClusters(spotPoint, k);
- bool centroidsChanged = true;
- int iterations = 0;
- while (centroidsChanged && iterations < maxIterations)
- {
- foreach (var i in clusters)
- i.Clear();
- foreach (var point in spotPoint)
- {
- int closestCentroidIndex = GetClosestClusterIndex(point, clusters);
- clusters[closestCentroidIndex].Add0(point);
- }
- centroidsChanged = false;
- foreach (var i in clusters)
- {
- if (i.UpdateCentroid())
- centroidsChanged = true;
- }
- iterations++;
- }
- // 聚类完成,添加泛光点
- foreach (var point in brightPoint)
- {
- int closestCentroidIndex = GetClosestClusterIndex(point, clusters);
- clusters[closestCentroidIndex].Add1(point);
- }
- return clusters;
- }
- private List<PixelSpotAreaOld> InitializeClusters(List<Vector2> points, int k)
- {
- Vector2[] centroids = new Vector2[k];
- HashSet<int> chosenIndices = new HashSet<int>();
- for (int i = 0; i < k; i++)
- {
- int index;
- do
- {
- index = Rand.Next(points.Count);
- } while (chosenIndices.Contains(index));
- chosenIndices.Add(index);
- centroids[i] = points[index];
- }
- List<PixelSpotAreaOld> clusters = new List<PixelSpotAreaOld>(k);
- foreach (var i in centroids)
- clusters.Add(new PixelSpotAreaOld(i));
- return clusters;
- }
- private int GetClosestClusterIndex(Vector2 point, List<PixelSpotAreaOld> centroids)
- {
- int closestIndex = 0;
- float minDistance = Vector2.Distance(point, centroids[0].Centroid);
- for (int i = 1; i < centroids.Count; i++)
- {
- float distance = Vector2.Distance(point, centroids[i].Centroid);
- if (distance < minDistance)
- {
- minDistance = distance;
- closestIndex = i;
- }
- }
- return closestIndex;
- }
- }
- }
|