用于显示遥感和常规数字图像的 Picturebox 控件
用于显示遥感和常规数字图像的 Picturebox 控件
引言
从遥感图像中提取有用的土地利用特征,例如建筑物、道路,甚至车辆,一直是数字图像分析领域的研究重点。如今,许多软件和网络服务(例如 Google Earth、Google Map、ArcMap 和 Bing Maps)已成功应用遥感图像信息来协助公众解决导航、城市规划和环境工程等各种问题。然而,与常规数字图像相比,遥感图像通常具有更多波段和更多样的数据类型(例如 byte
、integer
、float
和 double
)。由于遥感图像的特殊性,常规图像查看器对其支持不佳。例如,Visual Studio 中内置的 Picturebox
控件无法显示遥感图像。
本项目创建了一个新颖的 Picturebox
控件,该控件能够同时显示常规和遥感数字图像。与其他开源项目(如 MapWinGIS 和 SharpMap)相比,遥感 Picturebox
控件体积小巧,功能有限。然而,本项目提供了一个用于使用遥感图像的应用程序的图像显示控件,并且可能有助于启发初学者使用地理空间数据抽象库 (GDAL) 进行遥感图像视觉处理。该控件使用 GDAL 1.5 和 GDI+ 进行编译。GDAL 是一个强大且设计精良的开源库,可访问光栅格式的地理空间数据集。通过 GDAL 的 OGR 库也支持矢量格式的地理空间数据集。许多软件,例如 Google Earth、ArcGIS、ERDAS、GRASS 和 IDRISI,都使用了 GDAL 来显示和分析地理空间数据集。GDI+ 是一项成熟的技术,它使应用程序能够使用图形甚至格式化文本。它已被广泛用于基于 Windows 的应用程序。本项目使用 GDI+ 来显示遥感图像,因为它在显示 2D 图像方面效率很高。
背景
使用 GDI+ 渲染遥感图像的方法是使用来自相应波段的数据创建 Bitmap
对象。创建 Bitmap
对象最直接的方法是将遥感图像数据直接导入 BitmapData
对象。然而,通过完全导入遥感数据来创建 Bitmap
对象并不是一个明智的方法,因为加载遥感图像可能需要巨大的内存消耗。在该项目中,我使用了一种巧妙的方法来显示遥感图像,这种方法已被证明能够渲染大尺寸的遥感图像。图 2(a)显示了使用图像金字塔渲染遥感图像的方法,图 2(b)说明了在行和列方向上使用各种缓冲区比例渲染遥感图像的方法。为了节省内存,为遥感图像构建金字塔,根据内存限制或控件大小显示虚拟图层是一种常见的选择。然而,虚拟图层在行和列方向上的缩减比例相同(即 1/2、1/4、1/8、1/16),这通常会导致显示效果粗糙且模糊。此外,通过显示虚拟图层,对于初学者来说,在缩放和平移时很难明智地调整比例。与构建金字塔相比,使用不等权重缩放图像可以使用 GDAL 轻松实现(例如 Band.readraster
函数)。一旦确定了控件的显示矩形,用户就可以根据控件的显示尺寸导入遥感图像。本项目中使用了不安全代码(指针)来加速数据导入过程。
public Bitmap RSImg2BitMap(OSGeo.GDAL.Dataset dataset,
Rectangle ExtentRect, int[] displayBands)
{
int x1width = ExtentRect.Width;
int y1height = ExtentRect.Height;
Bitmap image = new Bitmap(x1width, y1height,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
int iPixelSize = 3;
if (dataset != null)
{
BitmapData bitmapdata = image.LockBits(new
Rectangle(0, 0, x1width, y1height),
ImageLockMode.ReadWrite, image.PixelFormat);
int ch = 0;
try
{
unsafe
{
for (int i = 1; i <= displayBands.Length; ++i)
{
OSGeo.GDAL.Band band =
dataset.GetRasterBand(displayBands[i - 1]);
int[] buffer = new int[x1width * y1height];
band.ReadRaster(0, 0, __Geodataset.RasterXSize,
__Geodataset.RasterYSize, buffer,
x1width, y1height, 0, 0);
int p_indx = 0;
if ((int)band.GetRasterColorInterpretation() == 5)
ch = 0;
if ((int)band.GetRasterColorInterpretation() == 4)
ch = 1;
if ((int)band.GetRasterColorInterpretation() == 3)
ch = 2;
if ((int)band.GetRasterColorInterpretation() != 2)
{
double maxVal = 0.0;
double minVal = 0.0;
maxVal = GetMaxWithoutNoData(dataset,
displayBands[i - 1], -9999.0);
minVal = GetMinWithoutNoData(dataset,
displayBands[i - 1], -9999.0);
for (int y = 0; y < y1height; y++)
{
byte* row = (byte*)bitmapdata.Scan0 +
(y * bitmapdata.Stride);
for (int x = 0; x < x1width; x++, p_indx++)
{
byte tempVal = shift2Byte(buffer[p_indx],
maxVal, minVal, -9999.0);
row[x * iPixelSize + ch] = tempVal;
}
}
}
else
{
double maxVal = 0.0;
double minVal = 0.0;
maxVal = GetMaxWithoutNoData(dataset,
displayBands[i - 1], -9999.0);
minVal = GetMinWithoutNoData(dataset,
displayBands[i - 1], -9999.0);
for (int y = 0; y < y1height; y++)
{
byte* row = (byte*)bitmapdata.Scan0 +
(y * bitmapdata.Stride);
for (int x = 0; x < x1width; x++, p_indx++)
{
byte tempVal = shift2Byte<int />(buffer[p_indx],
maxVal, minVal, -9999.0);
row[x * iPixelSize] = tempVal;
row[x * iPixelSize + 1] = tempVal;
row[x * iPixelSize + 2] = tempVal;
}
}
}
ch++;
}
}
}
finally
{
image.UnlockBits(bitmapdata);
}
}
return image;
}
创建你的应用程序
准备工作
- 将 DEMO 项目中的 5 个 .dll 文件(gdal15.dll、gdal_csharp.dll、gdalconst_wrap.dll、gdal_wrap.dll 和 gdalconst_csharp.dll)复制到你的可执行文件 bin 文件夹“..\bin\Debug\”,并将 gdal_csharp.dll 添加为项目引用。
- 通过选择项将 RSIMGControl.dll 添加到工具栏。
- 将名为
RSPicburebox
的控件拖到专用窗体上。 - 导入必要的库
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Text; using System.Windows.Forms; using RSIMGControl.control; using OSGeo.GDAL;
Using the Code
有两种方法可以使用 RSPicturebox
显示遥感图像。最简单的方法是使用以下代码设置“ImagePath
”属性
RSPicturebox rsPicburebox1=new RSPicturebox();
OpenFileDialog ofd = new OpenFileDialog();
//By default, assume that remote sensing images are usually
//saved in the format of.tif or .img
ofd.Filter = "regular digital image|*.jpg;*.png;*.bmp*|
remote sensing image|*.tif;*.img;*.int";
if (ofd.ShowDialog() == DialogResult.OK)
{
rsPicturebox1.ImagePath = ofd.FileName;
}
另一种方法是将 OSGeo.GDAL.Dataset
分配给 RSIMGControl
的 GeoDataset
属性,代码如下
RSPicturebox rsPicburebox1=new RSPicturebox();
OpenFileDialog ofd = new OpenFileDialog();
//By default, assume that remote sensing images are
//usually saved in the format //of .tif or .img
ofd.Filter = "regular digital image|*.jpg;*.png;*.bmp*|
remote sensing image|*.tif;*.img;*.int";
if (ofd.ShowDialog() == DialogResult.OK)
{
//Register all the drivers for remote sensing in different formats.
OSGeo.GDAL.Gdal.AllRegister();
//Open the image using "ReadOnly" access.
OSGeo.GDAL.Dataset dataset = OSGeo.GDAL.Gdal.Open
(__ImgPath, OSGeo.GDAL.Access.GA_ReadOnly);
rsPicburebox1.GeoDataset=dataset;
}
RGB 波段选择对于显示遥感图像也很重要,因为不同的土地利用特征会因不同的波段组合而得到强调。除了多光谱图像的默认设置(例如,红色波段是波段 1,绿色波段是波段 2,蓝色波段是波段 3)外,用户还可以使用以下语法指定 RGB 波段
//2, 3, 4 means band 2, 3, 4 for red, green and blue band, respectively.
int[] displayBands = new int[3] {2,3,4};
rsimgPicburebox1.DisplayBands =displayBands;
rsimgPicburebox1.Invalidate();
如果你将红色、绿色和蓝色波段都分配给同一个波段(例如,displayBands = new int[3] {1,1,1 };
),遥感图像将显示为灰度图像。与在 Google Earth 中查看图像一样,你可以使用鼠标滚轮放大和缩小显示图像,也可以通过按住鼠标右键移动鼠标来平移图像。
RSPicturebox
控件能够显示各种类型的图像,如 byte
、int16
、int32
、float
,甚至 double
。对于像素值超出显示范围(0-255)的遥感图像,像素值会被自动缩放。用户可以从作者的个人网站获取各种数据类型和格式的遥感样本图像,或从其他网站(USGS)搜索。需要澄清的是,无数据像素(例如,像素值 < -9999)将显示为黑色。此外,用户可以通过使用“GeoDataset
”属性访问遥感图像的数据,并通过“DispBitMap
”属性获取当前显示的图像。
关注点
演示中包含两个简单的应用程序,例如更改显示波段和直方图均衡化。
历史
- 第一个版本:2011 年 1 月
关于作者
我现在是 SUNY-ESF 地理空间信息科学与工程专业的博士生。我对应用地理空间数据集来解决前沿的环境和社会问题感兴趣。