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 InfraredSpotCluster(List spotPoint, List brightPoint, int spotMaxCount, Func PointFilter = null) { if (PointFilter != null) // 筛掉屏幕外的点 { var temp0 = new List(spotPoint.Count); var temp1 = new List(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[] ClustersArray = new List[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 points, List clusters) { int numPoints = points.Count; float totalSilhouetteScore = 0f; Dictionary pointToClusterMap = new Dictionary(); 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 Cluster(List spotPoint, List brightPoint, int k, int maxIterations = 10) { if (spotPoint.Count < k) return new List(); 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 InitializeClusters(List points, int k) { Vector2[] centroids = new Vector2[k]; HashSet chosenIndices = new HashSet(); 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 clusters = new List(k); foreach (var i in centroids) clusters.Add(new PixelSpotAreaOld(i)); return clusters; } private int GetClosestClusterIndex(Vector2 point, List 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; } } }