一:背景 1. 講故事 前段時間微信上有一位老朋友找到我,說他的程式跑著跑著記憶體會突然爆高,有時候會下去,有什麼會下不去,懷疑是不是某些情況下存在記憶體泄露,讓我幫忙分析一下,其實記憶體泄露方面的問題還是比較好解決的,看過這個dump之後覺得還是有一定的分享價值,拿出來和大家分享一下吧。 二:WinDb ...
C#使用GDAL庫需安裝以下兩個Nuget包:GDAL和GDAL.Native
也可以安裝這幾個包:
這兩種方式的註冊方式會不太一樣,其他用法大致相同。在此選中的是第一種方式,即在此安裝的是GDAL和GDAL.Native。
註冊
使用GDAL庫的功能之前,必須先註冊
// 註冊
GdalConfiguration.ConfigureGdal();
Gdal.AllRegister();
// 設置utf-8編碼
Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
若安裝的是第二種程式包,即MaxRev.Gdal.Core等,則註冊語句為:
// 註冊
GdalBase.ConfigureAll();
// 設置編碼
Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
需註意的是,有時會報"PROJ: proj_create_from_database: Cannot find proj.db"這樣的異常,解決這個異常,可以在註冊之後加上這行代碼,手動設置PROJ_DB環境變數的查找路徑:
Osr.SetPROJSearchPath(Environment.GetEnvironmentVariable("PROJ_LIB"));
或者在文件資源管理器中找到proj.db文件,再設置它的查找路徑
最後手動設置查找路徑:
Osr.SetPROJSearchPath("C:\\xxx\\xxx\\.nuget\\packages\\gdal.native\\3.5.3\\build\\gdal\\share");
MaxRev.Gdal的查找路徑為:
Osr.SetPROJSearchPath("C:\\xxx\\xxx\\.nuget\\packages\\maxrev.gdal.windowsruntime.minimal\\3.6.1.110\\runtimes\\win-x64\\native\\maxrev.gdal.core.libshared");
讀取信息
// 使用只讀方式打開tif文件
Dataset ds = Gdal.Open("C:\\xxx.tif", Access.GA_ReadOnly);
if (ds == null)
{
Console.WriteLine("文件打開失敗...");
return;
}
// 獲取驅動器名稱,這裡輸出的是:GeoTIFF
var driverName = ds.GetDriver().LongName;
// 投影坐標系信息
var projection = ds.GetProjection();
var projection = ds.GetProjectionRef();
這裡的波段表示單個柵格波段/通道/圖層,它不一定代表整個圖像,例如,24 位 RGB 圖像通常表示為具有三個波段的數據集,一個用於紅色,一個用於綠色,一個用於藍色。
// 波段數
var rasterCount = ds.RasterCount;
// 獲取tif的第一個波段,波段索引從1開始
var band = ds.GetRasterBand(1);
// 波段數據類型
var bandDataType = band.DataType;
Console.WriteLine("DataType: " + Gdal.GetDataTypeName(bandDataType));
// 波段顏色解釋
var interpretation = band.GetRasterColorInterpretation();
Console.WriteLine("PaletteInterp: " + interpretation.ToString());
讀取像素值
var band = ds.GetRasterBand(1);
// 計算最大值和最小值
double[] rasterMinMax = new double[2];
int approx_ok = 0;
band.ComputeRasterMinMax(rasterMinMax, approx_ok);
Console.WriteLine("rasterMinMax = " + rasterMinMax[0] + ", " + rasterMinMax[1]);
// 獲取最大值和最小值,這裡獲取的不一定是真實的最值
band.GetMaximum(out double mval, out int mhasval);
band.GetMinimum(out double min, out int minval);
// 獲取無效值
band.GetNoDataValue(out var val, out var hasval);
// 讀取像素值
int Xsize = band.XSize;
int Ysize = band.YSize;
var buff = new double[Xsize * Ysize];
band.ReadRaster(0, 0, Xsize, Ysize, buff, Xsize, Ysize, 0, 0);
for (int i = 0; i < Xsize; i++)
{
for (int j = 0; j < Ysize; j++)
{
var value = buff[i + j * Xsize];
Console.Write(value + " ");
}
}
計算經緯度
// 獲取仿射變換矩陣
/*
地理坐標和影像數據行列的轉換關係:
Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2)
Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5)
*/
double[] geoTrans = new double[6];
ds.GetGeoTransform(geoTrans);
// 轉換地理坐標系
SpatialReference spatial = ds.GetSpatialRef();
var geors = new SpatialReference("");
geors.ImportFromEPSG(4326);
var transformation = new CoordinateTransformation(spatial, geors);
//計算經緯度
for (int i = 0; i < Ysize; i++)
{
for (int j = 0; j < Xsize; j++)
{
// 計算投影坐標
var x = geoTrans[0] + i * geoTrans[1] + j * geoTrans[2];
var y = geoTrans[3] + i * geoTrans[4] + j * geoTrans[5];
// 計算經緯度
double[] longiAndLat = new double[2];
transformation.TransformPoint(longiAndLat, x, y, 0);
// 1是經度,2是緯度
Console.Write("(" + longiAndLat[1] + "," + longiAndLat[0] + ") , ");
}
}