在成功读取dataset之后,我们应该进行图像的显示,究竟如何把三个Band读取出来合成为标准的BMP图像呢?(读取dataset见:C# GDAL 数字图像处理Part1 安装以及读取Dataset_A_RSswjtuer的博客-CSDN博客)
首先,我们知道,读取的图像尺寸和PictureBox的尺寸总是不一样的,我们应该保持图像的原高宽比进行显示,才不至于导致图像的畸变。
比如出现这样的情况:
我们应该做的是,把Image的宽设置为Box的宽,然后根据Image原始的高宽比,求Image新的高(若Image是比Box高的也同理)。结果如下:
我们将保持高宽比显示后新的Image的宽和高称为BufferWidth和BufferHeight(Buffer:缓冲,缓冲区),它们就是我们创建BMP图像的宽和高。
之后,我们使用Band中的ReadRaster函数读取Band里的每一个像素的数据,此函数的参数如下:
ReadRaster(int xOff, int yOff, //读取的起始位置
int xSize, int ySize, //在原图中要读取的尺寸
int[] buffer, //接收数据的数组
int buf_xSize, int buf_ySize, //从原图中读取出来后变成的尺寸
int pixelSpace, int lineSpace) //读取时x和y方向上的间隔,即是否需要隔行读取
这里,如果从原图中读取数据的尺寸和最后Buffer的尺寸不一样,Gdal的函数会自动进行插值,最后把长度为 buf_xSize*buf_ySize 的数塞到buffer数组里,所以在定义buffer数组时,要仔细检查它的长度和 buf_xSize*buf_ySize 一致。
获取该波段的数据之后,把它逐个读取到Bitmap的R/G/B通道中即可,因为有三个通道,所以我们在显示彩色图像时需要三个波段,在显示单波段图像时把RGB都设为相同的值即可。
But!在原始的TIF或IMG数据中,读取出来的数值可能是超过了8Bit的数值,我们还需要将它缩放到0-255之间,这样才能顺利地合成BMP图像。这里作者提供两种简单的拉伸方法:
一、简单截断:
对于大于255的数值,我们全部设置为255;对于小于0的数值,全部设置为0。为了使这一函数更为灵活,我们把最大阈值和最小阈值变量化。函数如下:
private int clip_array(int min, int max,int Val) //截断
{
if (Val> max)
Val= max;
if (Val< min)
Val= min;
return Val;
}
这样,就是一个简单的截断函数。
二、简单拉伸:
简单拉伸是最基本的拉伸方法,它的公式如下:
这里的(255-0)也可以换成其他的想要变换成的范围的最大值减最小值。这个公式是计算原值在原范围内的百分比数,然后乘以新的范围,获得在新范围内的相应数值,如此即可缩放到新的范围内。在函数中,我们将“255”和“0”也变量化,使得函数更灵活。函数如下:
private int Linear_strentch(int Val,int[] Pminmax,int[] Nminmax)
{
//Pminmax是长度为2的数组,第一个数是原范围的Min,第二个数是原范围的Max
//Nminmax同理
return (int)((double)(Val - Pminmax[0]) / (double)(Pminmax[1] - Pminmax[0]) * (double)(Nminmax[1] - Nminmax[0]));
}
获得了合适的像素值之后,我们便可以通过BMP类的SetPixel函数将像素值设到某个像素的R/G/B通道中。
讲完了基本流程,我们来看看函数实现。
public System.Drawing.Bitmap Bands2BMP(OSGeo.GDAL.Dataset dt,int[] bands_idxs, int BoxWidth, int BoxHeight, int stretch = 0)
{
/*Paras:
dt: 图像的Dataset
bands_idxs: 要显示的波段的索引组成的数组,比如[1,2,3]就是从dt里抽取这三个波段进行合成图像
BoxWidth: 显示框的宽
BoxHeight: 显示框的高
stretch: 指定拉伸的方式
*/
foreach(int idx in bands_idxs)
{
if(idx > dt.RasterCount || idx < 1)
{
MessageBox.Show("这是一个编程错误,在三波段融合BMP中传入的波段值超出了Dataset的范围!", "错误!");
return null;
}
}
int ImgWidth = dt.RasterXSize;
int ImgHeight = dt.RasterYSize;
float ImgRatio = ImgWidth / (float)ImgHeight; //影像宽高比
float BoxRatio = BoxWidth / (float)BoxHeight; //显示控件宽高比
int BufferWidth, BufferHeight;
if (BoxRatio >= ImgRatio) //相同高度下Box更宽或一样宽,相同宽度下Box更矮
{
BufferHeight = BoxHeight;
BufferWidth = (int)(BufferHeight * ImgRatio);
}
else //相同高度下Box更窄,相同宽度下Box更高
{
BufferWidth = BoxWidth;
BufferHeight = (int)(BufferWidth / ImgRatio);
}
//这里进行的就是计算Buffer的尺寸
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
BufferWidth,
BufferHeight,
System.Drawing.Imaging.PixelFormat.Format24bppRgb
);//定义一定尺寸的BMP图
int[] R_data = new int[BufferWidth * BufferHeight];//存储在R通道显示的数据
OSGeo.GDAL.Band band1 = dt.GetRasterBand(bands_idxs[0]);//读取对应的波段
band1.ReadRaster(0, 0, ImgWidth, ImgHeight, R_data, BufferWidth, BufferHeight, 0, 0);//将波段的数据塞入存储数据的数组中
int[] G_data = new int[BufferWidth * BufferHeight];
OSGeo.GDAL.Band band2 = dt.GetRasterBand(bands_idxs[1]);
band2.ReadRaster(0, 0, ImgWidth, ImgHeight, G_data, BufferWidth, BufferHeight, 0, 0);
int[] B_data = new int[BufferWidth * BufferHeight];
OSGeo.GDAL.Band band3 = dt.GetRasterBand(bands_idxs[2]);
band3.ReadRaster(0, 0, ImgWidth, ImgHeight, B_data, BufferWidth, BufferHeight, 0, 0);
int i, j;
if(stretch == 0)//不拉伸
{
for (i = 0; i < BufferWidth; i++)
{
for (j = 0; j < BufferHeight; j++)
{
int rVal = this.clip_array(0, 255, System.Convert.ToInt32(R_data[i + j * BufferWidth])); //获得对应位置的数值
int gVal = this.clip_array(0, 255, System.Convert.ToInt32(G_data[i + j * BufferWidth]));
int bVal = this.clip_array(0, 255, System.Convert.ToInt32(B_data[i + j * BufferWidth]));
System.Drawing.Color newColor = System.Drawing.Color.FromArgb(rVal, gVal, bVal);
//Color类对象通过指定RGB的值构造
bitmap.SetPixel(i, j, newColor);
//使用SetPixel函数将指定位置的像素设置为指定Color
}
}
}
else if(stretch == 1) //简单拉伸
{
double[] maxandmin1 = { 0, 0 };
band1.ComputeRasterMinMax(maxandmin1, 0);
//ComputeRasterMinMax是Gdal的Band中自带的函数,参数一是需要我们定义的存储最大最小值的数组,参数二可以不用,设为0.
double[] maxandmin2 = { 0, 0 };
band2.ComputeRasterMinMax(maxandmin2, 0);
double[] maxandmin3 = { 0, 0 };
band3.ComputeRasterMinMax(maxandmin3, 0);
for (i = 0; i < BufferWidth; i++)
{
for (j = 0; j < BufferHeight; j++)
{
int rVal = System.Convert.ToInt32(R_data[i + j * BufferWidth]);
rVal = this.Linear_strentch(rVal, new int[2] { (int)maxandmin1[0], (int)maxandmin1[1] }, new int[2] { 0, 255 });
//将原范围拉伸至0-255之间。
int gVal = System.Convert.ToInt32(G_data[i + j * BufferWidth]);
gVal = this.Linear_strentch(gVal, new int[2] { (int)maxandmin2[0], (int)maxandmin2[1] }, new int[2] { 0, 255 });
int bVal = System.Convert.ToInt32(B_data[i + j * BufferWidth]);
bVal = this.Linear_strentch(bVal, new int[2] { (int)maxandmin3[0], (int)maxandmin3[1] }, new int[2] { 0, 255 });
System.Drawing.Color newColor = System.Drawing.Color.FromArgb(rVal, gVal, bVal);
bitmap.SetPixel(i, j, newColor);
}
}
}
return bitmap;
}
至此。