/// <summary> /// 辐射定标。 /// </summary> /// <param name="InputDS">输入的栅格数据集。</param> /// <param name="OutDataType">输出的数据类型。</param> /// <param name="Gain">增益倍数。</param> /// <param name="Offset">偏移量。</param> /// <param name="OutPath">输出栅格数据集的位置。</param> /// <returns>返回操作成功或失败。</returns> public static bool ApplyGainAndOffset(OSGeo.GDAL.Dataset InputDS, OSGeo.GDAL.DataType OutDataType, List <double> Gain, List <double> Offset, string OutPath) { try { if (InputDS == null) { throw new ArgumentNullException("输入数据集为空。"); } if (String.IsNullOrWhiteSpace(OutPath.Trim())) { throw new ArgumentNullException("输出路径为空或非法。"); } if ((Gain.Count != InputDS.RasterCount) || (Offset.Count != InputDS.RasterCount)) { throw new IndexOutOfRangeException("增益数据或偏移数据与输入数据集波段数不符。"); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } FrmProgress FP = new FrmProgress() { Text = "正在进行辐射定标...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); int xSize = InputDS.RasterXSize; int ySize = InputDS.RasterYSize; OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, InputDS.RasterCount, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(InputDS, DS); for (int i = 0; i < InputDS.RasterCount; i++) //遍历每个波段 { FP.SetProgress2("正在处理波段:", i + 1, InputDS.RasterCount, ""); for (int Row = 0; Row < ySize; Row++) //遍历每一行(y) { FP.SetProgress1("正在处理:", Row + 1, ySize, "行"); double[] Values = new double[xSize]; //读取DN到数组 InputDS.GetRasterBand(i + 1).ReadRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); for (int Col = 0; Col < xSize; Col++) //对每一个值进行计算 { Values[Col] = GainOffset(Values[Col], Gain[i], Offset[i]); } //写结果到新栅格 DS.GetRasterBand(i + 1).WriteRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } } FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// 数据集相除。 /// </summary> /// <param name="D1">被除数据集。</param> /// <param name="D2">除数据集。</param> /// <param name="OutDataType">输出数据集的数据类型。</param> /// <param name="OutPath">输出路径。</param> /// <returns>操作成功或失败。</returns> public static bool Divide(OSGeo.GDAL.Dataset D1, OSGeo.GDAL.Dataset D2, OSGeo.GDAL.DataType OutDataType, string OutPath) { try { if (D1 == null || D2 == 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 Width = Math.Min(D1.RasterXSize, D2.RasterXSize); int Height = Math.Min(D1.RasterYSize, D2.RasterYSize); int band = Math.Min(D1.RasterCount, D2.RasterCount); FrmProgress FP = new FrmProgress() { Text = "正在进行数据集除法运算...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, Width, Height, band, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(D1, DS); for (int bandcount = 1; bandcount <= band; bandcount++) { FP.SetProgress2("正在处理", bandcount, band, "波段"); for (int row = 0; row < Height; row++) { FP.SetProgress1("正在处理:", row + 1, Height, "行"); double[] d1Tmp = new double[Width]; double[] d2Tmp = new double[Width]; double[] dsTmp = new double[Width]; D1.GetRasterBand(bandcount).ReadRaster(0, row, Width, 1, d1Tmp, Width, 1, 0, 0); D2.GetRasterBand(bandcount).ReadRaster(0, row, Width, 1, d2Tmp, Width, 1, 0, 0); for (long i = 0; i < Width; i++) { if (d2Tmp[i] == 0) { dsTmp[i] = 0; } else { dsTmp[i] = d1Tmp[i] / d2Tmp[i]; } } DS.GetRasterBand(bandcount).WriteRaster(0, row, Width, 1, dsTmp, Width, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } } FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
public static bool CutImage(OSGeo.GDAL.Dataset InputDS, int CutWidth, int CutHeight, OSGeo.GDAL.DataType OutDataType, string OutFolder) { try { if (InputDS == null) { throw new ArgumentNullException("输入的数据集不可为空。"); } if (String.IsNullOrWhiteSpace(OutFolder)) { throw new ArgumentNullException("输出路径为空或非法。"); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } //取输入的宽、高、波段最小值创建输出数据集。 int Width = InputDS.RasterXSize; int Height = InputDS.RasterYSize; int Rows = Height / CutHeight; int Cols = Width / CutWidth; FrmProgress FP = new FrmProgress() { Text = "正在进行影像裁剪...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); FP.Output("输出的目录为:" + OutFolder); FP.Output("输出的数据类型为:" + OutDataType.ToString()); for (int row = 0; row < Rows; row++) { FP.SetProgress2("总进度:", row + 1, Rows, ""); string ParentPath = row.ToString(); Directory.CreateDirectory(Path.Combine(OutFolder, ParentPath)); for (int col = 0; col < Cols; col++) { FP.SetProgress1("当前行进度:", col + 1, Cols, ""); string OutName = row.ToString() + "_" + col.ToString() + ".tif"; OSGeo.GDAL.Dataset DS = Dri.Create(Path.Combine(OutFolder, ParentPath, OutName), CutWidth, CutHeight, InputDS.RasterCount, OutDataType, null); for (int band = 1; band <= InputDS.RasterCount; band++) { double[] buf = new double[CutWidth * CutHeight]; InputDS.GetRasterBand(band).ReadRaster(col * CutWidth, row * CutHeight, CutWidth, CutHeight, buf, CutWidth, CutHeight, 0, 0); DS.GetRasterBand(band).WriteRaster(0, 0, CutWidth, CutHeight, buf, CutWidth, CutHeight, 0, 0); } ////单行读取写入,防止爆内存。 //for (int cRow = 0; cRow < CutHeight; cRow++) //{ // double[] buf = new double[CutWidth]; // InputDS.GetRasterBand(band).ReadRaster(col * CutWidth, row * CutHeight + cRow, CutWidth, 1, buf, CutWidth, 1, 0, 0); // DS.GetRasterBand(band).WriteRaster(0, cRow, CutWidth, 1, buf, CutWidth, 1, 0, 0); // DS.FlushCache(); // Thread.Sleep(1); //} DS.FlushCache(); DS.Dispose(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } } FP.Finish(); InputDS.Dispose(); Dri.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// Brovey变换融合。融合前会将多光谱数据集的每一波段与全色数据集做直方图匹配。 /// </summary> /// <param name="PanDS">全色数据集。</param> /// <param name="MSDS">多光谱数据集</param> /// <param name="PanCumu">全色数据集的累积概率表。</param> /// <param name="MSCumu">多光谱数据集的累积概率表。</param> /// <param name="MSBandList">多光谱数据集的波段组合。</param> /// <param name="OutDataType">输出数据类型。</param> /// <param name="OutPath">输出路径。</param> /// <returns>返回操作成功或失败。</returns> public static bool Brovey(OSGeo.GDAL.Dataset PanDS, OSGeo.GDAL.Dataset MSDS, double[][] PanCumu, double[][] MSCumu, int[] MSBandList, OSGeo.GDAL.DataType OutDataType, string OutPath) { try { if (PanDS == null) { throw new ArgumentNullException("输入的全色数据集为空。"); } if (PanDS.RasterCount > 1) { throw new RankException("全色数据集波段大于1。"); } if (MSDS == 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 panWidth = PanDS.RasterXSize; int panHeight = PanDS.RasterYSize; int msWidth = MSDS.RasterXSize; int msHeight = MSDS.RasterYSize; //int rat = (int)Math.Ceiling((double)panHeight / msHeight); if (panWidth < msWidth) { throw new RankException("全色数据集宽度小于多光谱数据集宽度。"); } if (panHeight < msHeight) { throw new RankException("全色数据集高度小于多光谱数据集高度。"); } //if (rat <= 0) // throw new ArgumentOutOfRangeException("全色高度:多光谱高度小于1。"); FrmProgress FP = new FrmProgress() { Text = "正在进行Brovey融合...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); #region 预处理 //创建临时文件,进行直方图匹配。 string HisMatFileName = Tools.Common.GetTempFileName(); OSGeo.GDAL.Dataset MatchingDS = Dri.CreateCopy(HisMatFileName, MSDS, 0, null, null, null); FP.Output("已创建直方图匹配临时文件\"" + HisMatFileName + "\""); for (int band = 1; band <= MSDS.RasterCount; band++) { FP.Output("正在进行直方图匹配(" + band.ToString() + "/" + MSDS.RasterCount.ToString() + ")..."); Band b = Tools.BaseProcess.HistogramMatching(MSDS.GetRasterBand(band), PanDS.GetRasterBand(1), MSCumu[band - 1], PanCumu[0], OutDataType); if (b == null) { throw new ArgumentNullException("直方图匹配返回波段为空。"); } for (int row = 0; row < b.YSize; row++) { double[] tmp = new double[b.XSize]; b.ReadRaster(0, row, b.XSize, 1, tmp, b.XSize, 1, 0, 0); MatchingDS.GetRasterBand(band).WriteRaster(0, row, MatchingDS.GetRasterBand(band).XSize, 1, tmp, MatchingDS.GetRasterBand(band).XSize, 1, 0, 0); MatchingDS.FlushCache(); Thread.Sleep(1); } } //创建临时文件,进行重采样。 double[] resamptmp; string ResampFileName = Tools.Common.GetTempFileName(); OSGeo.GDAL.Dataset ResampDS = Dri.Create(ResampFileName, panWidth, panHeight, 3, MSDS.GetRasterBand(1).DataType, null); FP.Output("已创建重采样临时文件\"" + ResampFileName + "\""); //Tools.Common.CopyMetadata(PanDS, tmpDS); //Gdal.ReprojectImage(MSDS, tmpDS, null, null, 0, 0, 0, null, null); //将直方图匹配后的图像重采样。 for (int i = 0; i < 3; i++) { FP.Output("正在对直方图匹配后影像进行重采样(" + (i + 1).ToString() + "/" + "3)..."); resamptmp = new double[panWidth * panHeight]; MatchingDS.GetRasterBand(MSBandList[i]).ReadRaster(0, 0, msWidth, msHeight, resamptmp, panWidth, panHeight, 0, 0); ResampDS.GetRasterBand(i + 1).WriteRaster(0, 0, panWidth, panHeight, resamptmp, panWidth, panHeight, 0, 0); ResampDS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } //释放直方图匹配图像资源。 MatchingDS.Dispose(); if (File.Exists(HisMatFileName)) { File.Delete(HisMatFileName); } FP.Output("已删除直方图匹配临时文件"); #endregion //创建输出数据集。 OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, panWidth, panHeight, 3, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(PanDS, DS); double[] p; double[][] ms; double[] ds; for (int row = 0; row < panHeight; row++) { FP.SetProgress2("正在处理", row + 1, panHeight, "行"); //将全色波段行读取进数组。 p = new double[panWidth]; PanDS.GetRasterBand(1).ReadRaster(0, row, panWidth, 1, p, panWidth, 1, 0, 0); //将重采样后的多光谱行读取进数组。 ms = new double[3][]; ms[0] = new double[panWidth]; ms[1] = new double[panWidth]; ms[2] = new double[panWidth]; ResampDS.GetRasterBand(1).ReadRaster(0, row, panWidth, 1, ms[0], panWidth, 1, 0, 0); ResampDS.GetRasterBand(2).ReadRaster(0, row, panWidth, 1, ms[1], panWidth, 1, 0, 0); ResampDS.GetRasterBand(3).ReadRaster(0, row, panWidth, 1, ms[2], panWidth, 1, 0, 0); //遍历三波段 for (int bandcount = 0; bandcount < 3; bandcount++) { FP.SetProgress1("正在处理", bandcount + 1, 3, "波段"); ds = new double[panWidth]; //临时数组 for (long i = 0; i < panWidth; i++) { ds[i] = p[i] * ms[bandcount][i] / (ms[0][i] + ms[1][i] + ms[2][i]); //Brovey公式 } DS.GetRasterBand(bandcount + 1).WriteRaster(0, row, panWidth, 1, ds, panWidth, 1, 0, 0); //计算完后的数据写入 DS.FlushCache(); } Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } //释放重采样图像资源。 ResampDS.Dispose(); if (File.Exists(ResampFileName)) { File.Delete(ResampFileName); } FP.Output("已删除重采样临时文件"); /*一次性读取一时爽,遇到大文件爆内存。 * 一次性读取一时爽,遇到大文件爆内存。 * 一次性读取一时爽,遇到大文件爆内存。*/ //p = new double[panWidth * panHeight]; //ms = new double[3][]; //ms[0] = new double[panWidth * panHeight]; //ms[1] = new double[panWidth * panHeight]; //ms[2] = new double[panWidth * panHeight]; //PanDS.GetRasterBand(1).ReadRaster(0, 0, panWidth, panHeight, p, panWidth, panHeight, 0, 0); //MSDS.GetRasterBand(MSBandList[0]).ReadRaster(0, 0, msWidth, msHeight, ms[0], panWidth, panHeight, 0, 0); //MSDS.GetRasterBand(MSBandList[1]).ReadRaster(0, 0, msWidth, msHeight, ms[1], panWidth, panHeight, 0, 0); //MSDS.GetRasterBand(MSBandList[2]).ReadRaster(0, 0, msWidth, msHeight, ms[2], panWidth, panHeight, 0, 0); //for (int bandcount = 0; bandcount < 3; bandcount++) //{ // ds = new double[panWidth * panHeight]; // for (int row = 0; row < panHeight; row++) // { // for (int col = 0; col < panWidth; col++) // { // ds[row * panWidth + col] = p[row * panWidth + col] * ms[bandcount][row * panWidth + col] / (ms[0][row * panWidth + col] + ms[1][row * panWidth + col] + ms[2][row * panWidth + col]); // } // } // DS.GetRasterBand(bandcount + 1).WriteRaster(0, 0, panWidth, panHeight, ds, panWidth, panHeight, 0, 0); // DS.FlushCache(); //} FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// 影像直方图匹配。 /// </summary> /// <param name="InputDS">输入的数据集。</param> /// <param name="MatchDS">要匹配到的数据集。</param> /// <param name="InputCumu">输入的数据集的累积概率表。</param> /// <param name="MatchingCumu">要匹配到的数据集的累积概率表。</param> /// <param name="OutDataType">输出的数据类型。</param> /// <param name="OutPath">输出栅格数据集的位置。</param> /// <returns>操作成功或失败。</returns> public static bool HistogramMatching(OSGeo.GDAL.Dataset InputDS, OSGeo.GDAL.Dataset MatchDS, double[][] InputCumu, double[][] MatchingCumu, OSGeo.GDAL.DataType OutDataType, string OutPath) { try { if (InputDS.RasterCount != MatchDS.RasterCount) { throw new ArgumentException("数据集波段数不一致。"); } FrmProgress FP = new FrmProgress() { Text = "正在进行影像直方图匹配...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); int[][] LUT = new int[InputDS.RasterCount][]; FP.Output("正在计算查找表..."); for (int i = 0; i < InputDS.RasterCount; i++) { LUT[i] = HistogramMatching(InputCumu[i], MatchingCumu[i]); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } int xSize = InputDS.RasterXSize; int ySize = InputDS.RasterYSize; OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, InputDS.RasterCount, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(InputDS, DS); for (int bandcount = 0; bandcount < InputDS.RasterCount; bandcount++) { FP.SetProgress2("正在处理", bandcount + 1, InputDS.RasterCount, "波段"); Tools.Common.GetMinimum(InputDS.GetRasterBand(bandcount + 1), out double inputDSBandMin); Tools.Common.GetMinimum(MatchDS.GetRasterBand(bandcount + 1), out double matchDSBandMin); for (int Row = 0; Row < ySize; Row++) { FP.SetProgress1("正在处理", Row + 1, ySize, "行"); double[] Values = new double[xSize]; //读取DN到数组 InputDS.GetRasterBand(bandcount + 1).ReadRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); for (int Col = 0; Col < xSize; Col++) { //防止GDAL自带方法算出的统计数据越界。 if ((Values[Col] - inputDSBandMin) <= 0) { Values[Col] = matchDSBandMin + LUT[bandcount][0]; continue; } if ((Values[Col] - inputDSBandMin) >= LUT[bandcount].Length - 1) { Values[Col] = matchDSBandMin + LUT[bandcount][LUT[bandcount].Length - 1]; continue; } Values[Col] = matchDSBandMin + LUT[bandcount][(int)(Values[Col] - inputDSBandMin)]; } //写结果到新栅格 DS.GetRasterBand(bandcount + 1).WriteRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } } FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
public static bool CalculateFloodArea(string InDir, string OutDir) { try { if (!Directory.Exists(InDir)) { throw new FileNotFoundException("输入路径不存在。"); } if (!Directory.Exists(OutDir)) { Directory.CreateDirectory(OutDir); } OSGeo.GDAL.Driver Dri = OSGeo.GDAL.Gdal.GetDriverByName("Gtiff"); if (Dri == null) { throw new Exception("无法获取GDAL Driver。"); } FrmProgress FP = new FrmProgress() { Text = "正在处理洪水范围...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); bool createFlag = true; string[] ascFiles = Directory.GetFiles(InDir); int xSize = 0; int ySize = 0; OSGeo.GDAL.Dataset rangeDS = new OSGeo.GDAL.Dataset(IntPtr.Zero, false, null); OSGeo.GDAL.Dataset depthDS = new OSGeo.GDAL.Dataset(IntPtr.Zero, false, null); OSGeo.GDAL.Dataset timeDS = new OSGeo.GDAL.Dataset(IntPtr.Zero, false, null); for (int i = 0; i < ascFiles.Length; i++) { FP.SetProgress2("正在处理文件:", i + 1, ascFiles.Length, ""); string tmpPath = ascFiles[i]; FP.Output("正在处理:" + (i + 1).ToString() + ". " + tmpPath); if (tmpPath.Substring(tmpPath.LastIndexOf(".")) != ".txt") { FP.Output(tmpPath + "不是txt文件。"); continue; } OSGeo.GDAL.Dataset tmpDS = OSGeo.GDAL.Gdal.Open(tmpPath, OSGeo.GDAL.Access.GA_ReadOnly); if (createFlag) { xSize = tmpDS.RasterXSize; ySize = tmpDS.RasterYSize; string inDirName = Path.GetDirectoryName(tmpPath).Substring(Path.GetDirectoryName(tmpPath).LastIndexOf("\\") + 1); rangeDS = Dri.Create(Path.Combine(OutDir, inDirName + "_floodRange.tiff"), xSize, ySize, 1, OSGeo.GDAL.DataType.GDT_Float32, null); Tools.Common.CopyMetadata(tmpDS, rangeDS); FP.Output("已创建输出数据集\"" + Path.Combine(OutDir, inDirName + "_floodRange.tiff") + "\"。"); depthDS = Dri.Create(Path.Combine(OutDir, inDirName + "_floodDepth.tiff"), xSize, ySize, 1, OSGeo.GDAL.DataType.GDT_Float32, null); Tools.Common.CopyMetadata(tmpDS, depthDS); FP.Output("已创建输出数据集\"" + Path.Combine(OutDir, inDirName + "_floodDepth.tiff") + "\"。"); timeDS = Dri.Create(Path.Combine(OutDir, inDirName + "_floodTime.tiff"), xSize, ySize, 1, OSGeo.GDAL.DataType.GDT_Float32, null); Tools.Common.CopyMetadata(tmpDS, timeDS); FP.Output("已创建输出数据集\"" + Path.Combine(OutDir, inDirName + "_floodTime.tiff") + "\"。"); createFlag = false; } for (int Row = 0; Row < ySize; Row++) { double[] tmpValues = new double[xSize]; double[] rangeValues = new double[xSize]; double[] depthValues = new double[xSize]; double[] timeValues = new double[xSize]; tmpDS.GetRasterBand(1).ReadRaster(0, Row, xSize, 1, tmpValues, xSize, 1, 0, 0); rangeDS.GetRasterBand(1).ReadRaster(0, Row, xSize, 1, rangeValues, xSize, 1, 0, 0); depthDS.GetRasterBand(1).ReadRaster(0, Row, xSize, 1, depthValues, xSize, 1, 0, 0); timeDS.GetRasterBand(1).ReadRaster(0, Row, xSize, 1, timeValues, xSize, 1, 0, 0); for (int Col = 0; Col < xSize; Col++) { //计算洪水范围 if (Math.Abs(tmpValues[Col]) <= 1e-7) //Nodata { continue; } else { rangeValues[Col] = 1; if (tmpValues[Col] > depthValues[Col]) { depthValues[Col] = tmpValues[Col]; } timeValues[Col] += 1; } } rangeDS.GetRasterBand(1).WriteRaster(0, Row, xSize, 1, rangeValues, xSize, 1, 0, 0); rangeDS.FlushCache(); depthDS.GetRasterBand(1).WriteRaster(0, Row, xSize, 1, depthValues, xSize, 1, 0, 0); depthDS.FlushCache(); timeDS.GetRasterBand(1).WriteRaster(0, Row, xSize, 1, timeValues, xSize, 1, 0, 0); timeDS.FlushCache(); } tmpDS.Dispose(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } FP.Finish(); rangeDS.Dispose(); depthDS.Dispose(); timeDS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// 导出当前视图。 /// </summary> /// <param name="InputImg">影像对象。</param> /// <param name="OutDataType">输出数据类型。</param> /// <param name="OutPath">输出路径。</param> /// <returns>返回操作成功或失败。</returns> public static bool ExportView(Img InputImg, OSGeo.GDAL.DataType OutDataType, string OutPath) { try { if (OutPath == InputImg.Path) { throw new Exception("输出路径与源文件相同。"); } if (InputImg.GDALDataset == 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 xSize = InputImg.Width; int ySize = InputImg.Height; int Bandnum = 3; if (InputImg.IsGrayscale) { Bandnum = 1; } else { Bandnum = 3; } FrmProgress FP = new FrmProgress() { Text = "正在导出影像...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, Bandnum, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(InputImg.GDALDataset, DS); for (int i = 0; i < Bandnum; i++) //遍历每个波段 { FP.SetProgress2("正在处理", i + 1, Bandnum, "波段"); for (int Row = 0; Row < ySize; Row++) //遍历每一行(y) { FP.SetProgress1("正在处理", Row + 1, ySize, "行"); double[] Values = new double[xSize]; //读取DN到数组 InputImg.GDALDataset.GetRasterBand(InputImg.BandList[i]).ReadRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); for (int Col = 0; Col < xSize; Col++) //对每一个值进行计算 { //无拉伸不做处理直接导出 if (InputImg.StretchMode == 0) { break; } //已经有处理的情形 switch (InputImg.StretchMode) { case 1: case 2: case 3: case 4: case 9: { //线性 Values[Col] = LinearStretch(Values[Col], InputImg.StretchInfo[InputImg.BandList[i] - 1].Item1, InputImg.StretchInfo[InputImg.BandList[i] - 1].Item2); break; } case 5: { //直方图均衡 Values[Col] = EqualizationStretch(Values[Col], InputImg.CumulativeProbability[InputImg.BandList[i] - 1], InputImg.Min[InputImg.BandList[i] - 1], InputImg.Max[InputImg.BandList[i] - 1]); break; } case 6: { //高斯 Values[Col] = GaussianStretch(Values[Col], InputImg.CumulativeProbability[InputImg.BandList[i] - 1], InputImg.Min[InputImg.BandList[i] - 1], InputImg.Mean[InputImg.BandList[i] - 1], InputImg.Stddev[InputImg.BandList[i] - 1]); break; } case 7: { //平方根 Values[Col] = SquarerootStretch(Values[Col], InputImg.Min[InputImg.BandList[i] - 1], InputImg.Max[InputImg.BandList[i] - 1]); break; } case 8: { //对数 Values[Col] = LinearStretch(LogarithmicStretch(Values[Col], InputImg.Min[InputImg.BandList[i] - 1], InputImg.Max[InputImg.BandList[i] - 1]), InputImg.Min[InputImg.BandList[i] - 1], InputImg.Max[InputImg.BandList[i] - 1]); break; } default: { //不是0-9还能是啥? throw new Exception("拉伸模式未知。"); } } } //写结果到新栅格 DS.GetRasterBand(i + 1).WriteRaster(0, Row, xSize, 1, Values, xSize, 1, 0, 0); DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } } FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.Message); return(false); } }
/// <summary> /// 平滑锐化算法。 /// </summary> /// <param name="InputDS">输入的栅格数据集。</param> /// <param name="OutDataType">输出的数据类型。</param> /// <param name="Method">平滑锐化方法。</param> /// <param name="OutPath">输出栅格数据集的位置。</param> /// <returns>返回操作成功或失败。</returns> public static bool SmoothSharpening(OSGeo.GDAL.Dataset InputDS, OSGeo.GDAL.DataType OutDataType, SmoothShapeninngMethods Method, 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 xSize = InputDS.RasterXSize; int ySize = InputDS.RasterYSize; FrmProgress FP = new FrmProgress() { Text = "正在进行平滑锐化操作...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); FP.Output("选择的平滑/锐化方法为:\"" + (typeof(SmoothShapeninngMethods).GetField((Method).ToString()).GetCustomAttributes(false)[0] as DescriptionAttribute).Description + "\""); OSGeo.GDAL.Dataset DS = Dri.Create(OutPath, xSize, ySize, InputDS.RasterCount, OutDataType, null); FP.Output("已创建输出数据集\"" + OutPath + "\",数据类型为" + OutDataType.ToString() + "。"); Tools.Common.CopyMetadata(InputDS, DS); for (int band = 0; band < InputDS.RasterCount; band++) //遍历每个波段 { FP.SetProgress1("正在处理", band + 1, InputDS.RasterCount, "波段"); for (int Row = 0; Row < ySize - 2; Row++) //从第1行遍历至倒数第三行 { FP.SetProgress2("正在处理", Row, ySize - 2, "行"); double[] Values = new double[3 * xSize]; //读取的数值 double[] Result = new double[xSize]; //结果数值 InputDS.GetRasterBand(band + 1).ReadRaster(0, Row, xSize, 3, Values, xSize, 3, 0, 0); //读取3行 Array.Copy(Values, xSize, Result, 0, xSize); // for (int Col = 0; Col < xSize - 2; Col++) //对每一个值进行计算 { double[,] Input = new double[3, 3]; for (int i = 0; i < 3; i++) { Input[i, 0] = Values[Col + i * xSize]; Input[i, 1] = Values[Col + i * xSize + 1]; Input[i, 2] = Values[Col + i * xSize + 2]; } Result[Col + 1] = SmoothSharpening(Input, Method); //计算输入数据中心像元的值 } //写结果到新栅格 DS.GetRasterBand(band + 1).WriteRaster(0, Row + 1, xSize, 1, Result, xSize, 1, 0, 0); } if ((Method.ToString()).Contains("Smooth")) { double[] row = new double[xSize]; InputDS.GetRasterBand(band + 1).ReadRaster(0, 0, xSize, 1, row, xSize, 1, 0, 0); //读取第一行 DS.GetRasterBand(band + 1).WriteRaster(0, 0, xSize, 1, row, xSize, 1, 0, 0); InputDS.GetRasterBand(band + 1).ReadRaster(0, ySize - 1, xSize, 1, row, xSize, 1, 0, 0); //读取最后一行 DS.GetRasterBand(band + 1).WriteRaster(0, ySize - 1, xSize, 1, row, xSize, 1, 0, 0); } DS.FlushCache(); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } FP.Finish(); Dri.Dispose(); DS.Dispose(); return(true); } catch (Exception err) { MessageBox.Show(err.ToString()); return(false); } }
/// <summary> /// 迭代阈值法寻找分割阈值。 /// </summary> /// <param name="InputDS">输入的数据集。</param> /// <param name="ThresholdDiff">前后阈值之差的变化限值,小于此值则终止循环。</param> /// <returns>最优阈值。</returns> public static double IterateThreshold(OSGeo.GDAL.Dataset InputDS, double ThresholdDiff = 1) { try { if (InputDS == null) { throw new ArgumentNullException("输入数据集为空。"); } if (InputDS.RasterCount != 1) { throw new ArgumentException("输入的数据集波段大于1。"); } FrmProgress FP = new FrmProgress() { Text = "正在使用迭代阈值法寻找分割阈值...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); FP.Output("正在计算影像统计数据..."); Tools.Common.ComputeRasterMinMax(InputDS.GetRasterBand(1), out double Min, out double Max); //初始阈值及新阈值。 double tmpThreshold = (Min + Max) / 2.0; double newThreshold = 1000000; double Diff = 1000000; //迭代T值之差。 FP.Output("最小值:" + Min.ToString() + "\r\n最大值:" + Max.ToString() + "\r\n变化阈值:" + ThresholdDiff.ToString()); while (Diff >= ThresholdDiff) { FP.SetProgress1("阈值变化", ThresholdDiff, Diff, ""); //前景、背景像元平均值及计数。 double foregroundAvg = 0.0, backgroundAvg = 0.0; int foregroundCount = 0, backgroundCount = 0; for (int row = 0; row < InputDS.RasterYSize; row++) { FP.SetProgress2("迭代已处理", row + 1, InputDS.RasterYSize, "行"); double[] tmp = new double[InputDS.RasterXSize]; InputDS.GetRasterBand(1).ReadRaster(0, row, InputDS.RasterXSize, 1, tmp, InputDS.RasterXSize, 1, 0, 0); for (int col = 0; col < InputDS.RasterXSize; col++) { //逐像元累加前、背景计数和总和。 if (ThresholdCheck(tmp[col], tmpThreshold)) { foregroundCount++; foregroundAvg += tmp[col]; } else { backgroundCount++; backgroundAvg += tmp[col]; } } } foregroundAvg /= foregroundCount; backgroundAvg /= backgroundCount; newThreshold = (foregroundAvg + backgroundAvg) / 2.0; Diff = Math.Abs(newThreshold - tmpThreshold); //阈值差 tmpThreshold = newThreshold; FP.Output("新的阈值:" + newThreshold.ToString()); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } FP.Finish(); return(newThreshold); } catch (Exception err) { MessageBox.Show(err.Message); return(0); } }
/// <summary> /// Otsu(最大类间方差法)寻找分割阈值。 /// </summary> /// <param name="InputDS">输入的数据集。</param> /// <param name="Divide">将最大最小值区间内分割为的份数。</param> /// <returns>最优阈值。</returns> public static double Otsu(OSGeo.GDAL.Dataset InputDS, int Divide = 256) { try { if (InputDS == null) { throw new ArgumentNullException("输入数据集为空。"); } if (InputDS.RasterCount != 1) { throw new ArgumentException("输入的数据集波段大于1。"); } double[] g = new double[Divide]; FrmProgress FP = new FrmProgress() { Text = "正在使用Otsu法寻找分割阈值...", }; Thread t = new Thread(() => { FP.ShowDialog(); }); t.SetApartmentState(ApartmentState.STA); t.Start(); FP.Output("正在计算影像统计数据..."); Tools.Common.ComputeRasterMinMax(InputDS.GetRasterBand(1), out double Min, out double Max); FP.Output("最小值:" + Min.ToString() + "\r\n最大值:" + Max.ToString() + "\r\n分割段数:" + Divide.ToString()); for (int i = 1; i <= Divide; i++) { FP.SetProgress2("正在计算:", i, Divide, "区间"); double foregroundRatio = 0.0, foregroundAvg = 0.0, backgroundRatio = 0.0, backgroundAvg = 0.0; int foregroundCount = 0, backgroundCount = 0; double tmpThreshold = Min + (Max - Min) * ((double)i / Divide); for (int row = 0; row < InputDS.RasterYSize; row++) { FP.SetProgress1("正在处理:", row + 1, InputDS.RasterYSize, "行"); double[] tmp = new double[InputDS.RasterXSize]; InputDS.GetRasterBand(1).ReadRaster(0, row, InputDS.RasterXSize, 1, tmp, InputDS.RasterXSize, 1, 0, 0); for (int col = 0; col < InputDS.RasterXSize; col++) { //逐像元累加前、背景计数和总和。 if (ThresholdCheck(tmp[col], tmpThreshold)) { foregroundCount++; foregroundAvg += tmp[col]; } else { backgroundCount++; backgroundAvg += tmp[col]; } } } foregroundRatio = (double)foregroundCount / (foregroundCount + backgroundCount); foregroundAvg /= foregroundCount; backgroundRatio = (double)backgroundCount / (foregroundCount + backgroundCount); backgroundAvg /= backgroundCount; double totalGrayAvg = foregroundRatio * foregroundAvg + backgroundRatio * backgroundAvg; g[i - 1] = foregroundRatio * backgroundRatio * Math.Pow(foregroundAvg - backgroundAvg, 2); Thread.Sleep(1); if (FP.Canceled) { Thread.Sleep(500); FP.Finish(); throw new OperationCanceledException("操作被用户取消。"); } } //找一个最小值 int index = 0; double max = -100000000; for (int i = 0; i < Divide; i++) { if (g[i] > max) { max = g[i]; index = i; } } FP.Finish(); return(Min + (Max - Min) * ((double)index / (Divide - 1))); } catch (Exception err) { MessageBox.Show(err.Message); return(0); } }
/// <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); } }