public void getNodesCenter() { double sensorX = 0; double sensorY = 0; double halfRad = PublicParameters.clusterRadius / 2; double clusterX = this.clusterLocMargin.X + halfRad; double clusterY = this.clusterLocMargin.Y + halfRad; clusterActualCenter = new Point(clusterX, clusterY); double sumX = 0; double sumY = 0; double n = clusterNodes.Count; foreach (Sensor sensor in this.clusterNodes) { sensorX += sensor.CenterLocation.X; sensorY += sensor.CenterLocation.Y; } sumX = (double)clusterX + (sensorX / n); sumY = (double)clusterY + (sensorY / n); sumX = sumX / 2; sumY = sumY / 2; //double marginTop = Math.Floor(clusterY - this.clusterLocMargin.Y) - label_clustercenter.Height/2; // double marginLeft =Math.Floor( clusterX - this.clusterLocMargin.X) - label_clustercenter.Width/2; clusterCenterMargin = new Point(sumX, sumY); ClusterCenter center = new ClusterCenter(clusterCenterMargin, this.getID()); this.centerOfCluster = center; clusterCenterComputed = new Point(sumX, sumY); }
/// <summary> /// The greater the value of M, /// the more spatial proximity is emphasized and the more compact the cluster, /// M should be in range of 1 to 20. /// </summary> public static int[] ClusterPixels(ReadOnlySpan <Rgba32> pixels, int width, int height, int clusters, float m = 10, int maxIterations = 10, bool enforceConnectivity = true) { if (clusters < 2) { throw new ArgumentException("Number of clusters should be more than 1"); } //Grid interval S float S = MathF.Sqrt(pixels.Length / (float)clusters); int[] clusterIndices = new int[pixels.Length]; var labXys = ConvertToLabXy(pixels, width, height); Span <ClusterCenter> clusterCenters = InitialClusterCenters(width, height, clusters, S, labXys); Span <ClusterCenter> previousCenters = new ClusterCenter[clusters]; float Error = 999; const float threshold = 0.1f; int iter = 0; while (Error > threshold) { if (maxIterations > 0 && iter >= maxIterations) { break; } iter++; clusterCenters.CopyTo(previousCenters); Array.Fill(clusterIndices, -1); // Find closest cluster for pixels for (int j = 0; j < clusters; j++) { int xL = Math.Max(0, (int)(clusterCenters[j].x - S)); int xH = Math.Min(width, (int)(clusterCenters[j].x + S)); int yL = Math.Max(0, (int)(clusterCenters[j].y - S)); int yH = Math.Min(height, (int)(clusterCenters[j].y + S)); for (int x = xL; x < xH; x++) { for (int y = yL; y < yH; y++) { int i = x + y * width; if (clusterIndices[i] == -1) { clusterIndices[i] = j; } else { float prevDistance = clusterCenters[clusterIndices[i]].Distance(labXys[i], m, S); float distance = clusterCenters[j].Distance(labXys[i], m, S); if (distance < prevDistance) { clusterIndices[i] = j; } } } } } Error = RecalculateCenters(clusters, m, labXys, clusterIndices, previousCenters, S, ref clusterCenters); } if (enforceConnectivity) { clusterIndices = EnforceConnectivity(clusterIndices, width, height, clusters); } return(clusterIndices); }
/// <summary> /// ISO Data分类算法。 /// </summary> /// <param name="InputDS">输入的数据集。</param> /// <param name="MinKind">最小分类数。</param> /// <param name="MaxKind">最大分类数。</param> /// <param name="MaxItera">最大迭代次数。</param> /// <param name="CenterDiff">最大中心变化率</param> /// <param name="MinPixelNumInClass">类内最小像元数。</param> /// <param name="MaxClassStddev">最大类内标准差</param> /// <param name="MinClassDist">最小类间距离。</param> /// <param name="OutPath">结果数据集输出路径。</param> /// <returns>操作成功或失败。</returns> public static bool IsoData(OSGeo.GDAL.Dataset InputDS, int MinKind, int MaxKind, int MaxItera, double CenterDiff, int MinPixelNumInClass, double MaxClassStddev, double MinClassDist, string OutPath) { try { if (InputDS == null) { throw new ArgumentNullException("输入数据集为空。"); } if (String.IsNullOrWhiteSpace(OutPath.Trim())) { throw new ArgumentNullException("输出路径为空或非法。"); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } int bandNum = InputDS.RasterCount; int xSize = InputDS.RasterXSize; int ySize = InputDS.RasterYSize; FrmProgress FP = new FrmProgress() { Text = "正在进行ISO Data分类...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, 1, OSGeo.GDAL.DataType.GDT_Byte, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为GDT_Byte。"); Tools.Common.CopyMetadata(InputDS, DS); int tmpItera = 1; bool tmpContinue = true; double[] min = new double[bandNum]; double[] max = new double[bandNum]; double[] mean = new double[bandNum]; double[] stddev = new double[bandNum]; int InitKind = (MinKind + MaxKind) / 2; List <ClusterCenter> ICenter = new List <ClusterCenter>(); //计算数据集各波段统计数据 for (int i = 0; i < bandNum; i++) { Tools.Common.GetStatistics(InputDS.GetRasterBand(i + 1), out min[i], out max[i], out mean[i], out stddev[i]); } //初始化聚类中心 for (int i = 0; i < InitKind; i++) { ICenter.Add(new ClusterCenter(bandNum)); for (int band = 0; band < bandNum; band++) { ICenter[i].NewCenter[band] = ICenter[i].OldCenter[band] = mean[band] + stddev[band] - 2 * i * stddev[band] / InitKind; } } //循环体 while (tmpContinue) { FP.Output("正在迭代第" + tmpItera.ToString() + "次..."); FP.SetProgress2("正在迭代:", tmpItera, MaxItera, "次"); //清空所有聚类中心的和、平均值、标准差、像元列表 foreach (ClusterCenter C in ICenter) { C.ResetAll(); } //重置所有类的像元计数为0 for (int i = 0; i < ICenter.Count; i++) { ICenter[i].PixelCount = 0; } for (int i = 0; i < ICenter.Count; i++) { string tmpCenter = "第" + (i + 1).ToString() + "类中心:"; for (int band = 0; band < bandNum; band++) { tmpCenter += ICenter[i].OldCenter[band] + ", "; } FP.Output(tmpCenter); } //遍历数据集每一行 for (int row = 0; row < ySize; row++) { FP.SetProgress1("正在处理:", row + 1, ySize, "行"); double[][] tmpInput = new double[bandNum][]; //用于存储输入像元的交错数组 //将一行的所有波段像元值读取入交错数组 for (int band = 0; band < bandNum; band++) { tmpInput[band] = new double[xSize]; InputDS.GetRasterBand(band + 1).ReadRaster(0, row, xSize, 1, tmpInput[band], xSize, 1, 0, 0); } byte[] tmpOut = new byte[xSize]; //用于存储输出像元的数组 //遍历一行的每一列,对像元分类 for (int col = 0; col < xSize; col++) { double[] tmpPix = new double[bandNum]; //临时存储单个像元所有波段的数组 //将指定行列的所有波段像元转储到数组 for (int band = 0; band < bandNum; band++) { tmpPix[band] = tmpInput[band][col]; } double[] tmpDis = new double[ICenter.Count]; //单个像元到不同聚类中心的距离数组 //计算像元到不同聚类中心的距离 for (int i = 0; i < ICenter.Count; i++) { tmpDis[i] = Distance(tmpPix, ICenter[i].OldCenter); } double tmpMinDis = tmpDis[0]; //最小距离 int tmpClass = 0; //分类 //计算最小值及分类 for (int i = 1; i < ICenter.Count; i++) { if (tmpDis[i] < tmpMinDis) { tmpMinDis = tmpDis[i]; tmpClass = i; } } //将该像元添加到对应聚类的列表中 ICenter[tmpClass].CenterList.Add(tmpPix); //写入分类并增加对应类的像元计数 tmpOut[col] = (byte)(tmpClass + 1); ICenter[tmpClass].PixelCount += 1; } DS.GetRasterBand(1).WriteRaster(0, row, xSize, 1, tmpOut, xSize, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } //重新计算每个集群的均值和方差 foreach (ClusterCenter c in ICenter) { c.GetStddev(); //计算方差之前会自动求和和平均值 //将聚类中所有像元的平均值作为聚类的新中心? for (int band = 0; band < bandNum; band++) { c.NewCenter[band] = c.CenterMean[band]; } } for (int i = 0; i < ICenter.Count; i++) { string tmpCenter = "聚类后第" + (i + 1).ToString() + "类:"; tmpCenter += "\r\n\t像元数:" + ICenter[i].PixelCount + ", "; tmpCenter += "\r\n\t和:"; for (int band = 0; band < bandNum; band++) { tmpCenter += ICenter[i].CenterTotal[band] + ", "; } tmpCenter += "\r\n\t平均中心:"; for (int band = 0; band < bandNum; band++) { tmpCenter += ICenter[i].CenterMean[band] + ", "; } tmpCenter += "\r\n\t标准差:"; for (int band = 0; band < bandNum; band++) { tmpCenter += ICenter[i].CenterStddev[band] + ", "; } FP.Output(tmpCenter); } bool tmpCenterModified = false; //判断是否有聚类中心数量的变动 Delete: //删除像元数在阈值以下的聚类 for (int i = 0; i < ICenter.Count; i++) { if (ICenter[i].PixelCount < MinPixelNumInClass) { FP.Output("第" + (i + 1).ToString() + "类像元数只有" + ICenter[i].PixelCount.ToString() + "个,将被删除。"); if (ICenter.Count - 1 >= MinKind) { tmpCenterModified = true; ICenter.RemoveAt(i); goto Delete; } else { FP.Output("第" + (i + 1).ToString() + "类删除失败,已达到最小分类数。"); goto EndDelete; } } } EndDelete :; //合并与分裂 List <ClusterCenter> tmpChangeCenter = new List <ClusterCenter>(); //用于暂存对全局列表的更改 //偶次合并 if (tmpItera % 2 == 0) { Combine: for (int i = 0; i < ICenter.Count; i++) { for (int j = 0; j < ICenter.Count; j++) { //不与自身比较 if (i == j) { continue; } //类间距离小于设定阈值,进行合并 if (Distance(ICenter[i].CenterMean, ICenter[j].CenterMean) < MinClassDist) { FP.Output("第" + (i + 1).ToString() + "类和第" + (j + 1).ToString() + "类的距离过小,为" + Distance(ICenter[i].CenterMean, ICenter[j].CenterMean) + ",将被合并。"); if (ICenter.Count - 1 >= MinKind) { tmpCenterModified = true; tmpChangeCenter.Add(new ClusterCenter(ICenter[i], ICenter[j])); //合并两类并在临时列表中新建中心。 //考虑i、j的大小,依次删除全局列表中对应索引处的中心(从大的开始删除)。 if (i > j) { ICenter.RemoveAt(i); ICenter.RemoveAt(j); } else { ICenter.RemoveAt(j); ICenter.RemoveAt(i); } //每次迭代是只合并一次嘛?如果是的话就不需要goto goto Combine; } else { FP.Output("第" + (i + 1).ToString() + "类和第" + (j + 1).ToString() + "类合并失败,已达到最小分类数。"); goto EndCombine; } } } } EndCombine :; } else //奇次分裂 { Split : for (int i = 0; i < ICenter.Count; i++) { if (ICenter[i].MaxStddev > MaxClassStddev) { FP.Output("第" + (i + 1).ToString() + "类的类内最大方差为" + ICenter[i].MaxStddev.ToString() + ",将被分裂。"); if (ICenter.Count + 1 <= MaxKind) { tmpCenterModified = true; ClusterCenter C1 = new ClusterCenter(bandNum); ClusterCenter C2 = new ClusterCenter(bandNum); //新建两聚类,中心为旧中心加减方差的一半 //分裂后中心的参数怎么算? for (int band = 0; band < bandNum; band++) { //只改变大于标准差的中心值,若不大于则保持不变。 if (ICenter[i].CenterStddev[band] > MaxClassStddev) { C1.OldCenter[band] = C1.NewCenter[band] = ICenter[i].NewCenter[band] + ICenter[i].CenterStddev[band] / 2; C2.OldCenter[band] = C2.NewCenter[band] = ICenter[i].NewCenter[band] - ICenter[i].CenterStddev[band] / 2; } else { C1.OldCenter[band] = C1.NewCenter[band] = ICenter[i].NewCenter[band]; C2.OldCenter[band] = C2.NewCenter[band] = ICenter[i].NewCenter[band]; } //C1.OldCenter[band] = C1.NewCenter[band] = ICenter[i].NewCenter[band] + MaxClassStddev / 2; //C2.OldCenter[band] = C2.NewCenter[band] = ICenter[i].NewCenter[band] - MaxClassStddev / 2; } tmpChangeCenter.Add(C1); tmpChangeCenter.Add(C2); ICenter.RemoveAt(i); goto Split; } else { FP.Output("第" + (i + 1).ToString() + "类分裂失败,已达到最大分类数。"); goto EndSplit; } } } EndSplit :; } double tmpCenterChangeMax = 100000000; //仅在聚类中心数量没有变化的时候才计算中心变化率 if (!tmpCenterModified) { double[] tmpCenterChange = new double[ICenter.Count]; //计算聚类中心变化率 for (int i = 0; i < ICenter.Count; i++) { for (int band = 0; band < bandNum; band++) { if (Math.Abs(ICenter[i].OldCenter[band]) <= 1e-7) //分母为0 { tmpCenterChange[i] += Math.Abs(ICenter[i].NewCenter[band] - ICenter[i].OldCenter[band]) / 0.0001; } else { tmpCenterChange[i] += Math.Abs(ICenter[i].NewCenter[band] - ICenter[i].OldCenter[band]) / ICenter[i].OldCenter[band]; } } tmpCenterChange[i] /= bandNum; } //计算最大变化率 tmpCenterChangeMax = tmpCenterChange[0]; for (int i = 1; i < ICenter.Count; i++) { if (tmpCenterChange[i] > tmpCenterChangeMax) { tmpCenterChangeMax = tmpCenterChange[i]; } } FP.Output("中心变化率:" + tmpCenterChangeMax + ",阈值:" + CenterDiff); FP.SetProgress1("最大中心变化率:", CenterDiff, tmpCenterChangeMax, ""); } else { FP.Output("第" + tmpItera.ToString() + "次迭代出现聚类中心的合并或分裂,不计算中心变化率..."); } //将新的中心变为旧的中心,准备开始新一轮迭代 for (int i = 0; i < ICenter.Count; i++) { for (int band = 0; band < bandNum; band++) { ICenter[i].OldCenter[band] = ICenter[i].NewCenter[band]; } } //将临时新建的聚类中心添加到全局聚类中心的末尾,并清空临时聚类中心 ICenter.AddRange(tmpChangeCenter); tmpChangeCenter.Clear(); tmpItera++; //判断是否继续循环。 if (tmpItera % 2 == 0) { if (((MaxItera > 1) && (tmpItera > MaxItera)) || (!tmpCenterModified && tmpCenterChangeMax < CenterDiff) || (tmpItera > 10000)) { tmpContinue = false; } } } FP.Finish(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// K-Means算法。 /// </summary> /// <param name="InputDS">输入的数据集。</param> /// <param name="Kind">分类数。</param> /// <param name="MaxItera">最大迭代次数。</param> /// <param name="CenterDiff">聚类中心变化率阈值。</param> /// <param name="OutPath">输出路径。</param> /// <returns>操作成功或失败。</returns> public static bool KMeans(OSGeo.GDAL.Dataset InputDS, int Kind, int MaxItera, double CenterDiff, string OutPath) { try { if (InputDS == null) { throw new ArgumentNullException("输入数据集为空。"); } if (String.IsNullOrWhiteSpace(OutPath.Trim())) { throw new ArgumentNullException("输出路径为空或非法。"); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } int bandNum = InputDS.RasterCount; int xSize = InputDS.RasterXSize; int ySize = InputDS.RasterYSize; FrmProgress FP = new FrmProgress() { Text = "正在进行K-Means分类...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, 1, OSGeo.GDAL.DataType.GDT_Byte, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为GDT_Byte。"); Tools.Common.CopyMetadata(InputDS, DS); int tmpItera = 0; bool tmpContinue = true; double[] min = new double[bandNum]; double[] max = new double[bandNum]; double[] mean = new double[bandNum]; double[] stddev = new double[bandNum]; ClusterCenter[] KCenter = new ClusterCenter[Kind]; //计算数据集各波段统计数据 for (int i = 0; i < bandNum; i++) { Tools.Common.GetStatistics(InputDS.GetRasterBand(i + 1), out min[i], out max[i], out mean[i], out stddev[i]); } //初始化聚类中心 for (int i = 0; i < Kind; i++) { KCenter[i] = new ClusterCenter(bandNum); for (int band = 0; band < bandNum; band++) { KCenter[i].NewCenter[band] = KCenter[i].OldCenter[band] = mean[band] + stddev[band] - 2 * i * stddev[band] / Kind; } } //循环体 while (tmpContinue) { FP.Output("正在迭代第" + (tmpItera + 1).ToString() + "次..."); FP.SetProgress2("正在迭代:", tmpItera + 1, MaxItera, "次"); //重置所有类的像元计数为1 for (int i = 0; i < Kind; i++) { KCenter[i].PixelCount = 1; } for (int i = 0; i < Kind; i++) { string tmpCenter = "第" + (i + 1).ToString() + "类中心:\r\n"; for (int band = 0; band < bandNum; band++) { tmpCenter += "\t第" + (band + 1).ToString() + "波段中心:" + KCenter[i].OldCenter[band] + "\r\n"; } FP.Output(tmpCenter); } //遍历数据集每一行 for (int row = 0; row < ySize; row++) { FP.SetProgress1("正在处理:", row + 1, ySize, "行"); double[][] tmpInput = new double[bandNum][]; //用于存储输入像元的交错数组 //将一行的所有波段像元值读取入交错数组 for (int band = 0; band < bandNum; band++) { tmpInput[band] = new double[xSize]; InputDS.GetRasterBand(band + 1).ReadRaster(0, row, xSize, 1, tmpInput[band], xSize, 1, 0, 0); } byte[] tmpOut = new byte[xSize]; //用于存储输出像元的数组 //遍历一行的每一列,对像元分类 for (int col = 0; col < xSize; col++) { double[] tmpPix = new double[bandNum]; //临时存储单个像元所有波段的数组 //将指定行列的所有波段像元转储到数组 for (int band = 0; band < bandNum; band++) { tmpPix[band] = tmpInput[band][col]; } double[] tmpDis = new double[Kind]; //单个像元到不同聚类中心的距离数组 //计算像元到不同聚类中心的距离 for (int i = 0; i < Kind; i++) { tmpDis[i] = Distance(tmpPix, KCenter[i].OldCenter); } double tmpMinDis = tmpDis[0]; //最小距离 int tmpClass = 0; //分类 //计算最小值及分类 for (int i = 1; i < Kind; i++) { if (tmpDis[i] < tmpMinDis) { tmpMinDis = tmpDis[i]; tmpClass = i; } } //更新聚类中心 for (int band = 0; band < bandNum; band++) { KCenter[tmpClass].NewCenter[band] = (KCenter[tmpClass].NewCenter[band] * KCenter[tmpClass].PixelCount + tmpPix[band]) / (KCenter[tmpClass].PixelCount + 1); } //写入分类并增加对应类的像元计数 tmpOut[col] = (byte)(tmpClass + 1); KCenter[tmpClass].PixelCount += 1; } DS.GetRasterBand(1).WriteRaster(0, row, xSize, 1, tmpOut, xSize, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } for (int i = 0; i < Kind; i++) { string tmpCenter = "聚类后第" + (i + 1).ToString() + "类:\r\n\t新中心:"; for (int band = 0; band < bandNum; band++) { tmpCenter += KCenter[i].NewCenter[band] + ", "; } FP.Output(tmpCenter); } double[] tmpCenterChange = new double[Kind]; //计算聚类中心变化率 for (int i = 0; i < Kind; i++) { for (int band = 0; band < bandNum; band++) { if (Math.Abs(KCenter[i].OldCenter[band]) <= 1e-7) //分母为0 { tmpCenterChange[i] += Math.Abs(KCenter[i].NewCenter[band] - KCenter[i].OldCenter[band]) / 0.0001; } else { tmpCenterChange[i] += Math.Abs(KCenter[i].NewCenter[band] - KCenter[i].OldCenter[band]) / KCenter[i].OldCenter[band]; } } tmpCenterChange[i] /= bandNum; } //计算最大变化率 double tmpCenterChangeMax = tmpCenterChange[0]; for (int i = 1; i < Kind; i++) { if (tmpCenterChange[i] > tmpCenterChangeMax) { tmpCenterChangeMax = tmpCenterChange[i]; } } FP.Output("中心变化率:" + tmpCenterChangeMax + ",阈值:" + CenterDiff); FP.SetProgress1("最大中心变化率:", CenterDiff, tmpCenterChangeMax, ""); //将新的中心变为旧的中心,准备开始新一轮迭代 for (int i = 0; i < Kind; i++) { for (int band = 0; band < bandNum; band++) { KCenter[i].OldCenter[band] = KCenter[i].NewCenter[band]; } } tmpItera++; //判断是否继续循环 if (((MaxItera > 1) && (tmpItera > MaxItera)) || (tmpCenterChangeMax < CenterDiff) || (tmpItera > 10000)) { tmpContinue = false; } } FP.Finish(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }