尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

VS2008下用GDAL读取TIFF影像并显示:从编译到RasterIO实战

VS2008下用GDAL读取TIFF影像并显示:从编译到RasterIO实战 简介一份基于VS2008与C环境、借助GDAL库读取并显示TIFF影像的入门级工程源码包面向GIS开发初学者、遥感数据处理人员以及需要在毕业设计或项目起步阶段快速上手GDAL的C开发者。资源共45个文件压缩包约14.31MB其中头文件与cpp源码负责核心逻辑sln、vcproj为VS2008工程配置exe为可直接运行示例obj、pdb、ilk等为编译中间输出另有rc、ico、bmp等界面资源。已有800人学习下载。工程清晰演示了通过GDALDriverManager获取GTiff驱动、打开数据集、访问GDALDataset与GDALRasterBand并调用ReadRaster读取像素数据、按色彩解释转为可见影像的完整流程同时包含项目属性配置、关闭与缓存刷新等细节可帮助理解GDAL核心对象与C项目集成方式。压缩包内还包含ReadMe.txt说明便于快速熟悉环境在此基础之上还能进一步扩展图像裁剪、重采样、颜色校正与坐标转换等功能。 VS2008、C、GDAL库、TIFF影像这几个词凑在一起基本就是一个很典型的“老项目改造”场景。前阵子帮朋友处理一个维护了快十年的地质影像浏览模块界面是用VS2008写的MFC程序底层需要直接打开TIFF格式的卫星影像和扫描地质图原来的实现是项目早期自己写的一个简易TIFF解析器只支持未压缩的8位灰度图一旦遇到压缩的、16位的、或者带坐标信息的GeoTIFF就直接罢工。朋友找我的时候那个模块正被一堆新数据折磨得没法用。我接手后的第一反应就是别自己再造轮子了直接把GDAL库引进来。GDAL是地理空间数据领域的事实标准对TIFF的支持是它骨子里的能力背后有完整的libtiff实现压缩、分块、多波段、16位浮点、地理配准这些全都替你处理好了。你唯一要做的就是学会怎么在VS2008这个老编译器环境里把GDAL折腾起来然后用对RasterIO那几个参数。这篇文章就把我整个过程中踩过的坑、验证过能跑的写法、以及那些只会在实战里遇到的细节完整记录一遍。1. 从一台旧机器说起为什么VS2008里还得用GDAL读TIFF1.1 需求场景与环境清单先说说我面对的实际环境。那套系统部署在现场工控机上Windows XP系统开发环境是VS2008 SP1项目代码是纯C的MFC程序UI层用的是经典GDI。因为整个系统经过了多年的定制和维护迁移到新IDE的成本远高于继续在VS2008上做增量开发所以“引入GDAL库”不是想选哪个版本就选哪个版本而是要在这个老的编译环境里跑通。再看数据侧。现场拿到的TIFF五花八门有普通8位三波段RGB影像有16位单波段的DEM高程数据还有带地理参考的GeoTIFF。大多数文件来自无人机航拍和卫星遥感原始数据常常是LZW压缩或者Deflate压缩的。这些格式如果自己写解析器工作量会非常恐怖。GDAL用一行注册函数就能全部搞定它把压缩解码、格式识别、采样策略都封装在内部你在应用层根本不需要关心它是怎么解压的。1.2 为什么不用其他方案有朋友可能会说用CxImage、FreeImage之类的库不也能读TIFF确实能但GDAL有它不可替代的位置。首先GDAL对地理信息的处理是其他图像库做不到的GeoTIFF里面的坐标变换、投影参数、像元分辨率这些信息对工程测量类的应用很关键其次GDAL的RasterIO是一个极灵活的接口可以用任意尺寸的缓冲去读取原始影像的任意区域这意味着你可以在不完全加载全图的情况下按需读取画面中的一块来显示这对大影像来说几乎是救命的特性。FreeImage在纯图像解码上不差但遇到超大影像的局部访问和重采样操作起来远不如GDAL顺手。1.3 明确边界这篇文章做的是显示不是分析我要先划清一个范围这篇文章要解决的核心问题是“把TIFF影像像素显示到窗口上”。也就是打开文件、读取像素、转成GDI能识别的位图、画出来。至于投影转换、矢量叠加、坐标配准这些更深的东西我只在最后提一下怎么把地理参数读出来不展开做完整的GIS功能。别小看这个范围界定实际开发中很多人就是在这里搞混了精力分配一上来就想做地图级别的功能结果连最底层的像素显示都没跑通。2. GDAL库的编译与VS2008工程依赖配置2.1 版本选择VS2008能顺利编译的GDALVS2008对应的Visual C编译器版本是9.0这是2008年发布的老编译器。GDAL的新版本比如3.x系列已经要求更高版本的Visual Studio才能编译因为在代码里用到了较新的C标准特性。我在这个项目里选的是GDAL 1.11.5这个版本在VS2008下编译完全无压力而且对TIFF的所有常见需求都支持得很好。如果你的场景里需要更新的格式驱动可以试试GDAL 2.2.x系列但我个人建议没必要追新1.11.5足够稳定还有一个好处是接口用起来简单直接C绑定和C接口都清晰。2.2 nmake编译全过程我习惯的做法是下载源码自己编译因为VS2008的年代太久远官方发布的预编译DLL基本都是配新版运行时的直接拿来用容易在运行期出莫名其妙的崩溃。源码编译其实不复杂步骤如下。打开“Visual Studio 2008 命令提示符”注意不是普通的cmd要在这个环境里才能找到nmake和cl进入GDAL源码解压目录先编辑nmake.opt文件把安装路径改掉GDAL_HOME C:\gdal\gdal-1.11.5\dist然后依次执行nmake /f makefile.vc nmake /f makefile.vc install第一条命令编译整个GDAL库这个过程会持续一段时间取决于机器性能通常要十几分钟到半小时。第二条命令把编译好的头文件、库文件、DLL和命令行工具安装到前面设置的GDAL_HOME目录里。完成后你会得到四个关键东西include目录下的一堆头文件、lib目录下的gdal_i.lib导入库、bin目录下的gdal111.dll以及gdalinfo、gdal_translate等命令行工具。这里有几点实践经验值得记录。第一如果你在命令行里发现编译报“cl不是内部或外部命令”说明你没有打开VS2008的命令行环境而是打开了普通cmd。第二默认编译的是32位版本如果你确实需要64位的需要打开“Visual Studio 2008 x64 Win64 命令提示符”重新来一遍但实际工程多数是32位不用折腾。第三GDAL的makefile默认编译的是Release版本这和我们后面要说的工程运行时配置息息相关。2.3 VS2008工程里的依赖设置库编译好之后就是把你自己的VS2008工程配置好。在项目属性的“配置属性 → C/C → 常规 → 附加包含目录”里填入GDAL的include目录在“配置属性 → 链接器 → 常规 → 附加库目录”里填入GDAL的lib目录然后在“链接器 → 输入 → 附加依赖项”里加上gdal_i.lib。为了省事我一般还会在代码开头加一句#pragma comment(lib, gdal_i.lib)这样就不需要在工程UI里反复设置了。最后把gdal111.dll复制到exe的输出目录不然程序启动时会提示找不到DLL。2.4 一个能让你省三天排查时间的配置坑这一节说实话是我最想强调的。VS2008工程的默认“运行时库”配置Debug版本是“多线程调试DLL(/MDd)”Release版本是“多线程DLL(/MD)”。而GDAL的nmake默认编译结果是调用动态运行时库的。如果你在VS2008里建了一个Debug版工程直接链接Release版GDAL运行时库不匹配的隐患就埋下了。短期内可能一切正常但一旦GDAL内部在DLL里分配了一块内存而你在exe这边用不同运行时的方式去释放程序就会在运行一段时间后随机崩溃这种问题极难定位很多资深开发者都会被坑一晚上。我的做法是不管Debug还是Release工程都把“运行时库”统一设置为“多线程DLL(/MD)”Debug版会因此损失一些调试能力但换来了稳定。如果你舍不得debug运行时那就专门去编译一份GDAL的Debug版库注意别把Release的混进来。这两个策略选一个宁可麻烦一次不要埋雷。3. 读取TIFF像素RasterIO那几个参数才是关键3.1 初始化和打开文件先把最基础的打开流程写出来。代码看起来很简单但每一步都有讲究。#include gdal_priv.h #include cstdio #pragma comment(lib, gdal_i.lib) int main() { GDALAllRegister(); CPLSetConfigOption(GDAL_FILENAME_IS_UTF8, NO); GDALDataset* poDataset (GDALDataset*)GDALOpen(D:\\test.tif, GA_ReadOnly); if (!poDataset) { printf(open failed\n); return -1; } int nWidth poDataset-GetRasterXSize(); int nHeight poDataset-GetRasterYSize(); int nBands poDataset-GetRasterCount(); GDALDataType eType poDataset-GetRasterBand(1)-GetRasterDataType(); printf(width%d, height%d, bands%d, type%s\n, nWidth, nHeight, nBands, GDALGetDataTypeName(eType)); GDALClose(poDataset); return 0; }必须说两个容易翻车的地方。第一GDALAllRegister这个注册函数不能漏不调用它GDAL连GTiff驱动都不知道你打开任何文件都会失败。第二那一行CPLSetConfigOption(GDAL_FILENAME_IS_UTF8, NO)在中文Windows系统下极端重要。GDAL默认认为文件名是UTF-8编码但Windows的中文系统上你从CString直接转出来的往往是ANSI/GBK编码路径不关掉这个选项带中文目录的TIFF文件就打不开。很多人在这一步卡住明明文件路径是对的程序却提示打开失败。3.2 灰度图读取单波段RasterIO对于8位灰度TIFF读取方式最直接。拿到第一个波段然后整幅读进来GDALRasterBand* poBand poDataset-GetRasterBand(1); unsigned char* pGray new unsigned char[nWidth * nHeight]; poBand-RasterIO(GF_Read, 0, 0, nWidth, nHeight, pGray, nWidth, nHeight, GDT_Byte, 0, 0); // 用完之后 delete[] pGray;这个函数的前三个参数是源影像的起始行列号接着两个参数是源影像读取宽高然后是指向目标缓冲区的指针之后两个是目标缓冲区的宽高再后面是目标缓冲区数据类型。这里源影像宽高和缓冲区宽高都是一样的意思是全幅读取、不缩放。最后一个参数先不用管单波段读取的时候填0就可以。3.3 RGB彩色图读取理解pixelSpace、lineSpace、bandSpace到了彩色图情况就变得微妙了。最直观的笨办法是分三次读取三个波段然后手工合并成RGB数组。但GDAL提供了更优雅的机制一次调用就能把多个波段的数据交织读到缓冲区里。这里必须理解三个间距参数。pixelSpace同一个波段内相邻两个像素在缓冲区里的字节间隔 lineSpace同一个波段内相邻两行同列像素在缓冲区里的字节间隔 bandSpace同一个像素位置上相邻两个波段在缓冲区里的字节间隔先看一个常用的交织读取例子。假设存储方式是 RGBRGBRGB那么一个像素的三个通道是紧挨着的所以bandSpace等于1即蓝色通道的数据就存在红色通道下一个字节的位置从一个像素的红色通道到下一个像素的红色通道中间隔了3个字节所以pixelSpace等于3每行宽度为nWidth一行下来隔了nWidth3个字节所以lineSpace等于nWidth3。代码是这样写的int nSize nWidth * nHeight; unsigned char* pImg new unsigned char[nSize * 3]; int anBandMap[3] {3, 2, 1}; // 三个波段的映射 poDataset-RasterIO(GF_Read, 0, 0, nWidth, nHeight, pImg, nWidth, nHeight, GDT_Byte, 3, anBandMap, 3, nWidth * 3, 1);这里有个细节是别人很少讲的GDAL读取三波段交织数据时默认的顺序是波段1、2、3也就是RGB。但Windows GDI显示真彩色DIB时内存格式却是BGR。很多人在这一行被坑影像显示出来之后红蓝互换颜色极其诡异。解决方法是把波段映射表写成{3, 2, 1}让GDAL直接按BGR顺序读到pImg里这样后续显示时就不用再做一次像素数据的交换了。这个技巧在写代码时就要想清楚不要等画出来才发现。3.4 提前判断波段数分情况读取真实世界的TIFF千奇百怪。有时候你以为它是RGB图打开一看波段数只有1是一个灰度影像有时候它看起来是彩色的实际上是带调色板的索引图。所以在读取之前一定要先判断波段数然后走对应的分支波段数为1读灰度显示时转成8位灰度DIB波段数为2通常第二个波段是Alpha或附加数据可以先按灰度显示第一个波段波段数为3按RGB/BGR方式读取和显示波段数为4一般是RGB加Alpha通道可以读前三个波段或者把Alpha信息提取出来做透明处理我在项目里就遇到过同一批数据里混着灰度TIFF和RGB TIFF的情况如果不做判断程序在读取时会直接报错或者显示成一片花屏。这个问题必须在代码层面防御掉。4. 显示到屏幕BGR顺序和DIB位深不能搞错4.1 构建BITMAPINFO像素数据读出来之后接下来的事情就是把它们画到窗口上。在MFC程序里经典的方案是用SetDIBitsToDevice函数。这个函数接收一个指向BITMAPINFO的指针和一个像素缓冲区直接把DIB数据画到设备上下文上。先看24位真彩色的情况。假设你已经按照上一节的方式把数据读成了BGR交织格式那么构建BITMAPINFO就很简单BITMAPINFO bmi {0}; bmi.bmiHeader.biSize sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth nWidth; bmi.bmiHeader.biHeight -nHeight; // 注意负号 bmi.bmiHeader.biPlanes 1; bmi.bmiHeader.biBitCount 24; bmi.bmiHeader.biCompression BI_RGB; ::SetDIBitsToDevice(hDC, 0, 0, nWidth, nHeight, 0, 0, 0, nHeight, pImg, bmi, DIB_RGB_COLORS);这里有个非常容易犯的错误biHeight的正负号。如果biHeight是正数GDI会认为这是bottom-up位图图像的第一行在内存的最后面如果biHeight取负数则表示top-down位图图像第一行就在内存最前面。GDAL读取的影像顺序是标准的从上到下也就是第一行是影像顶部所以显示时必须使用负的biHeight否则图像会上下颠倒。这个坑我见过不止一次屏幕上的地理影像倒过来一整版数据看着都很诡异。4.2 灰度图需要额外构造颜色表8位灰度图的BITMAPINFO构建和24位不一样。8位DIB需要一个颜色表把像素值映射成RGB颜色。如果你不设置这个调色板GDI会默认使用系统调色板图像显示出来往往是一团乱麻。下面的代码展示了怎么正确构建BITMAPINFO* pBmi (BITMAPINFO*)new BYTE[sizeof(BITMAPINFOHEADER) 256 * sizeof(RGBQUAD)]; memset(pBmi, 0, sizeof(BITMAPINFOHEADER) 256 * sizeof(RGBQUAD)); pBmi-bmiHeader.biSize sizeof(BITMAPINFOHEADER); pBmi-bmiHeader.biWidth nWidth; pBmi-bmiHeader.biHeight -nHeight; pBmi-bmiHeader.biPlanes 1; pBmi-bmiHeader.biBitCount 8; pBmi-bmiHeader.biClrUsed 256; for (int i 0; i 256; i) { pBmi-bmiColors[i].rgbRed (BYTE)i; pBmi-bmiColors[i].rgbGreen (BYTE)i; pBmi-bmiColors[i].rgbBlue (BYTE)i; pBmi-bmiColors[i].rgbReserved 0; } ::SetDIBitsToDevice(hDC, 0, 0, nWidth, nHeight, 0, 0, 0, nHeight, pGray, pBmi, DIB_RGB_COLORS); delete[] (BYTE*)pBmi;注意BITMAPINFO结构体在Windows SDK里只自带一个RGBQUAD数组元素要存256个调色板项必须按上面的方式申请变长内存。4.3 关于双缓冲和闪烁直接调用SetDIBitsToDevice在窗口上绘制在数据量小的时候感觉还行但图像尺寸一大、刷新一频繁画面就会闪烁得很厉害。原因很简单每次绘制都直接在屏幕上更新而窗口在收到WM_ERASEBKGND时还会先擦掉背景一擦一画之间用户就会看到闪烁。标准的解法是做内存DC双缓冲。先创建一个兼容DC在内存里创建一个同样大小的位图先把影像绘制到这个内存DC上然后再一次性BitBlt到窗口DC。我在这里给一个简化的思路CDC memDC; CBitmap memBitmap; memDC.CreateCompatibleDC(pDC); memBitmap.CreateCompatibleBitmap(pDC, nWndWidth, nWndHeight); CBitmap* pOldBitmap memDC.SelectObject(memBitmap); // 在 memDC 上执行 SetDIBitsToDevice SetDIBitsToDevice(memDC.GetSafeHdc(), ...); pDC-BitBlt(0, 0, nWndWidth, nWndHeight, memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOldBitmap);这一步看似多花了一点内存开销但对用户体验的提升是立竿见影的。老项目里很多显示模组没有用双缓冲一拖动滚动条就屏幕乱闪加上双缓冲之后立刻干净了。4.4 缩放显示StretchDIBits如果影像尺寸比窗口大你需要在显示时做缩放。最简单的办法是用StretchDIBits替代SetDIBitsToDevice。这个函数多了一套目标矩形的参数可以让GDI在绘制时自动放大缩小::StretchDIBits(hDC, dstX, dstY, dstW, dstH, 0, 0, nWidth, nHeight, pImg, bmi, DIB_RGB_COLORS, SRCCOPY);第一次用的时候觉得省事但要注意这种缩放的效率在数据量很大的时候并不好。它每画一帧都要把全幅数据做一次缩放计算如果影像达到几千万像素一帧可能要几百毫秒拖动起来非常卡。真正的优化方案还是回到GDAL这边用RasterIO直接读一个和目标窗口尺寸相当的缓冲区出来再把这块小图贴到屏幕上。这样GDAL内部会用金字塔和重采样算法处理缩放性能能提升好几个量级。5. 大影像、16位影像与容易忽略的元数据5.1 直接读全图的内存陷阱很多第一次用GDAL的人都会写一个最简单的读取逻辑不管图像多大一次性RasterIO读全图像素然后转换成位图显示。这个逻辑在小图上没问题但换成一张50000乘40000像素的航拍TIFF单是8位三波段数据像素缓冲区就需要50000乘40000乘3字节算下来约6GB内存。程序瞬间就OOM崩溃了。我在这类影像查看项目里确立了一个原则显示窗口能看到的区域才是真正需要读取的区域。如果整幅影像只有几千乘几千全图读取可以接受一旦影像超过屏幕分辨率的数倍就必须按窗口尺寸来做局部读取。做法是在滚动或缩放时把当前视口相对于原影像的像素范围算出来然后用RasterIO只读这一块int nWinX, nWinY, nWinW, nWinH; // 当前视口在原影像中的像素矩形 int nViewW, nViewH; // 视口的目标缓冲大小 poDataset-RasterIO(GF_Read, nWinX, nWinY, nWinW, nWinH, pViewBuf, nViewW, nViewH, GDT_Byte, 3, anBandMap, 3, nViewW * 3, 1);这种情况下内存占用只和视口大小相关和影像总大小无关。配合双缓冲绘制即使打开几个GB的TIFF也能流畅操作。5.2 缩略图和金字塔让你的程序跑出专业感还有一个实用技巧我想单独拿出来说。预览窗口或缩略图需要的分辨率通常只有几百乘几百如果每次都用高分辨率读全图再缩小速度依然很慢。其实RasterIO允许你指定一个和源影像分辨率不同的读取尺寸GDAL会自动做重采样。比如一张一万乘一万的影像你想生成400宽度的缩略图可以这样int nThumbW 400; int nThumbH (int)(nHeight * 400.0 / nWidth); unsigned char* pThumb new unsigned char[nThumbW * nThumbH * 3]; poDataset-RasterIO(GF_Read, 0, 0, nWidth, nHeight, pThumb, nThumbW, nThumbH, GDT_Byte, 3, anBandMap, 3, nThumbW * 3, 1);注意这里的差异源尺寸是一万乘一万缓冲区尺寸是400乘300左右。RasterIO会自动做像素抽样或者平均。如果影像本身带有金字塔概览文件.ovr这一步会走概览层速度快到几乎无感。生成.ovr可以用GDAL自带的命令行工具gdaladdo也可以在程序里调用GDAL的API创建。我在项目里都会在第一次打开大影像时检查旁边是否有同名.ovr没有就调用一次概览生成后续浏览体验会好很多。5.3 16位影像的显示拉伸16位单波段TIFF在遥感数据里太常见了尤其是DEM和原始卫星影像。这类数据的一个特点是直接用8位显示会一片漆黑或一片惨白因为有效的像素值可能集中在1000到3000这个范围内直接强转成0到255的字节值绝大多数像素都落在了黑或者白那一端。正确做法是先在原数据上做一次统计找出最小值和最大值然后把像素值线性映射到0到255。可以用GDAL的统计接口GDALRasterBand* poBand poDataset-GetRasterBand(1); double dfMin, dfMax; poBand-GetStatistics(TRUE, TRUE, dfMin, dfMax);如果你不想等它统计也可以自己遍历一遍像素找极值。反正思路一致得到有效区间后再逐像素计算映射值。16位数据读出来是unsigned short数组显示前需要这样处理unsigned short* pRaw new unsigned short[nWidth * nHeight]; poBand-RasterIO(GF_Read, 0, 0, nWidth, nHeight, pRaw, nWidth, nHeight, GDT_UInt16, 0, 0); unsigned char* pGray new unsigned char[nWidth * nHeight]; double dScale 255.0 / (dfMax - dfMin 1); for (int i 0; i nWidth * nHeight; i) { double v (pRaw[i] - dfMin) * dScale; pGray[i] v 0 ? 0 : (v 255 ? 255 : (unsigned char)v); }这一步不做16位影像显示出来基本没法看。我甚至建议把这个拉伸逻辑做成三个模式自动拉伸、百分比拉伸去掉两端的1%极值、手动设置最小最大。自动拉伸用统计数据的最大最小百分比拉伸能避免个别亮斑把整幅图的色调压暗这两个模式在查看影像时都非常实用。5.4 波段顺序、nodata与地理坐标最后聊几个容易被忽略但迟早要面对的细节。波段顺序问题我在前面已经强调过一次这里再说一个场景。有些影像数据是RGBA四通道也就是RGB加Alpha。如果你直接把四波段全部送去显示GDI会认为每四个字节是一个像素画出完全错乱的颜色。正确做法是只读前三个波段忽略Alpha。而遇到单波段的彩色调色板TIFF比如扫描的工程图纸导出的索引色TIFF你就需要额外读调色板再转换成RGB真彩色来显示否则画出来就是黑白的。nodata值也是遥感数据的老熟人。很多TIFF在无效区域填一个特定的值比如-9999或者0。显示的时候如果不处理可能会出现一个巨大的黑色矩形或白色区域非常影响观感。GDAL提供了读取nodata值的接口int bGotNoData FALSE; double dNoData poBand-GetNoDataValue(bGotNoData);拿到nodata后在像素映射或显示阶段把它设置成透明或者指定的背景色是专业影像浏览器的基本素养。至于地理坐标信息读取方式很简单。GDALDataset有两个方法double adfGeoTransform[6]; if (poDataset-GetGeoTransform(adfGeoTransform) CE_None) { // 影像左上角横坐标 adfGeoTransform[0] // 像素宽度 adfGeoTransform[1] // 旋转参数 adfGeoTransform[2] // 影像左上角纵坐标 adfGeoTransform[3] // 旋转参数 adfGeoTransform[4] // 像素高度 adfGeoTransform[5]通常为负数 } const char* pszWkt poDataset-GetProjectionRef();pszWkt返回的是OGC WKT格式的投影描述adfGeoTransform则给出了影像像素坐标到地图坐标的仿射变换关系。对于普通的“显示TIFF”需求这两个值用不上但如果你的系统后续要做叠加、量测或者导出带坐标的成果图这些参数就是地基。我习惯在打开文件后就把这些信息打印出来或者存到日志里方便排查问题。5.5 大文件读取与资源释放的几个习惯GDALDataset在使用完之后一定要调用GDALClose而不是delete这是新手最容易错的地方。GDALDataset的析构函数在内部做了很多资源回收和缓存刷新的工作直接delete会造成内存泄露或者未定义行为。另外大影像处理时的内存分配释放频率很高我推荐用完一块像素缓冲就立刻释放不要等到函数末尾统一清理。因为大影像处理峰值内存可能很高不及时释放会导致程序内存占用居高不下最终被系统拖慢甚至报错。写成局部作用域或智能指针都是不错的选择VS2008环境虽然不方便用现代C的unique_ptr但在局部函数块里手动delete也不难做到。说实话在做完这个大影像显示模块之后我最大的感受是GDAL这套库的门槛不在API本身而在你愿不愿意花时间理解“按需读取”和“像素格式转换”这两个核心问题。RasterIO看着就是一堆参数理解了pixelSpace、lineSpace、bandSpace你就掌握了GDAL数据读取的钥匙GDI显示看着就是SetDIBitsToDevice理解了BGR顺序和biHeight正负号你就避开了显示阶段九成以上的坑。剩下的无非是遇到问题先从gdalinfo里看数据特征再决定用哪条读取路径。这套组合拳打下来VS2008里的TIFF显示任务也就没那么多悬念了。本文还有配套的精品资源点击获取
返回列表