当前位置: 代码迷 >> 综合 >> GDAL 读写图片
  详细解决方案

GDAL 读写图片

热度:77   发布时间:2024-01-28 13:26:48.0

使用GDAL打开和保存常见格式图像(代码)

https://blog.csdn.net/godenlove007/article/details/8864763


习惯了使用OpenCV的cvLoadImage函数和imread函数打开图像,但是貌似老师不喜欢opencv,实验室也用惯了GDAL,于是乎,就搜集各种资源,整理了使用GDAL读写图像的代码。

1.使用GDAL打开常见格式图像,并且保存到一维数组中
首先,需要判定图像后缀名,因为GDAL使用不同的驱动器打开对应的图像格式文件,笔者写了个支持JPG、BMP、PNG、GIF、TIFF格式判断的函数。注意,GDAL实际上可以说是个万能的图像格式转换库了,支持几十种图像格式,但是我们一般人常用的也就这么几种。

调用GDAL驱动器,打开图像,送进内存数组。
/******************************************************************************
函数名:
readImageGDAL
功能:
读取图像
参数:
unsigned char **pImageData - 指向图像数据指针的指针,将由new操作符动态创建,需要在函数外部由调用者使用delete[]销毁,否则内存泄露
int &width,int &height     - 图像宽度、高度,由于是引用,可以作为返回值。
nChannels                  - 图像通道,可选值为1或3。1代表灰度图像,3代表RGB图像,-1表示按照图像自身通道数目读取。
const char *filePath       - 图像文件路径名称
说明:******************************************************************************/
bool readImageGDAL(unsigned char **pImageData,int &width,int &height,int &nChannels, const char *filePath)
{
GDALAllRegister();
GDALDataset *poDataset = NULL;
poDataset=(GDALDataset*) GDALOpen(filePath,GA_ReadOnly);    
if(poDataset == NULL)
{
GDALClose(poDataset);
return false;
}
width = poDataset->GetRasterXSize();
height = poDataset->GetRasterYSize();
GDALRasterBand* pBand;
int i = 0;
int nRastercount = poDataset->GetRasterCount();
if (nRastercount == 1) //只有1个通道,则为灰度图像
{
nChannels = 1;
pBand = poDataset->GetRasterBand(1); 
*pImageData = new unsigned char[width * height];
pBand->RasterIO(GF_Read,
0,0,            //nXOff,nYOff:从左上角坐标point(nXOff,nYOff)处读取图像数据
width,height,    //nXSize,nXSize:要读取的图像数据尺寸,注意可以不是band的实际尺寸,这样就是读取roi区域数据
*pImageData,    //pData:读取的数据即将存储的目的地址。
width,height,    //nBufXSize,nBufYSize:目的地址处图像的尺寸,如果与roi尺寸不一致,则缩放。
GDT_Byte,        //eBufType:读取图像后,将要存储为的类型
0,                //nPixelSpace:控制同一行相邻像素间隔,0代表使用目的数据类型大小
0);                //nLineSpace:控制相邻行的行间隔,0代表使用[目的数据类型大小 * nXsize]
GDALClose(poDataset);
return true;
}
else if ( nRastercount == 3 && (nChannels == 3 || nChannels < 0) ) //有3个通道,并且输出为RGB图像
{        
nChannels = 3;
*pImageData = new unsigned char[nRastercount * width * height];        
for (i = 1; i <= nRastercount; ++ i)
{
//GDAL内band存储顺序为RGB,需要转换为我们一般的BGR存储,即低地址->高地址为:B G R
unsigned char *pImageOffset = *pImageData + i - 1;
GDALRasterBand* pBand = poDataset->GetRasterBand(nRastercount-i+1);

pBand->RasterIO(
GF_Read,
0,0,            
width,height,    
pImageOffset,    
width,height,    
GDT_Byte,        
3,                
0);                
}
GDALClose(poDataset);
return true;
}
else if ( nRastercount == 3 && nChannels == 1 ) //有3个通道,但是要求输出灰度图像
{
unsigned char **img = new unsigned char*[nRastercount];
for (i = 0; i < nRastercount; i++)
{
img[i] = new unsigned char[width * height];
}
for (i = 1; i <= nRastercount; ++ i)
{
//GDAL内band存储顺序为RGB,需要转换为我们一般的BGR存储,即低地址->高地址为:B G R
pBand = poDataset->GetRasterBand(nRastercount-i+1); 
pBand->RasterIO(GF_Read,
0,0,
width,height,
img[i-1],
width,height,
GDT_Byte,
0,
0);
}
GDALClose(poDataset);
*pImageData = new unsigned char[width*height];
for (int r = 0; r < height; ++ r)
{
for (int c = 0; c < width; ++ c)
{
int t = (r*width+c);
//r g b分量依次占:0.299 0.587 0.144,可简化为3:6:1
//img[1.2.3]依次对应BGR
(*pImageData)[t] = (img[2][t]*3 + img[1][t]*6 + img[0][t] + 5)/10;
}
}

for (i = 0; i < nRastercount; ++ i)
{
delete [] img[i];
}
delete []img; img = NULL;
return true;
}
else 
{
return false;
}
}

以上代码是比较长,不过实现的功能适合cvLoadImage函数功能差不多的。特别是nChannels参数,如果在输入中指定为1,则不管图像文件时彩色还是灰度图像,都会转换为成为灰度图像存储在数组中,如果指定为3,则只有图像时彩色图像时,才会真的存成3通道图像。如果指定为负值,则按照图像的实际通道数目读取。
另外,注意一点,这个图像只读上述各种格式的灰度图像和3通道图像,其它的通道数目不支持。

2.保存图像到文件中
2.1根据文件后缀名判断图像格式。
函数代码如下:

char* findImageTypeGDAL( char *pDstImgFileName)
{
char *dstExtension = strlwr(strrchr(pDstImgFileName,'.') + 1);
char *Gtype = NULL;
if        (0 == strcmp(dstExtension,"bmp")) Gtype = "BMP";
else if (0 == strcmp(dstExtension,"jpg")) Gtype = "JPEG";
else if (0 == strcmp(dstExtension,"png")) Gtype = "PNG";
else if (0 == strcmp(dstExtension,"tif")) Gtype = "GTiff";
else if (0 == strcmp(dstExtension,"gif")) Gtype = "GIF";
else Gtype = NULL;

return Gtype;
}
呵呵,太弱智了。
2.2保存图像代码
bool WriteImageGDAL(const char* pDstImgFileName,bool *pImageData,int width,int height,int nChannels)
{
assert ( !(pDstImgFileName == NULL || pImageData == NULL || width <1 || height < 1 || nChannels < 1));

GDALAllRegister();
char *GType = NULL;
GType = findImageTypeGDAL(pDstImgFileName);
if (GType == NULL)    { return false; }

GDALDriver *pMemDriver = NULL;
pMemDriver = GetGDALDriverManager()->GetDriverByName("MEM");
if( pMemDriver == NULL ) { return false; }

GDALDataset * pMemDataSet = pMemDriver->Create("",width,height,nChannels,GDT_Byte,NULL);
GDALRasterBand *pBand = NULL;
int nLineCount = width * nChannels;
unsigned char *ptr1 = (unsigned char *)pImageData;
for (int i = 1; i <= nChannels; i++)
{
pBand = pMemDataSet->GetRasterBand(nChannels-i+1);
pBand->RasterIO(GF_Write, 
0, 
0, 
width, 
height, 
ptr1+i-1 , 
width, 
height, 
GDT_Byte, 
nChannels, 
nLineCount); 
}

GDALDriver *pDstDriver = NULL;
pDstDriver = (GDALDriver *)GDALGetDriverByName(GType);
if (pDstDriver == NULL) { return false; }

pDstDriver->CreateCopy(pDstImgFileName,pMemDataSet,FALSE, NULL, NULL, NULL);

GDALClose(pMemDataSet); 

return true; 
}
保存的结果文件格式,通过其后缀名给出。
3.补充说明
调用的GDAL库可在网上下载,在上述代码前部加入以下包含命令:
#include &quot;gdal.h&quot;
#include &quot;gdal_priv.h&quot;
#pragma comment(lib,&quot;gdal_i.lib&quot;)</pre>
其中,gdal_i.lib是GDAL库的导出库文件,另外还需要制定对应的dll文件的path路径。