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

习惯了使用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 "gdal.h"
#include "gdal_priv.h"
#pragma comment(lib,"gdal_i.lib")
其中,gdal_i.lib是GDAL库的导出库文件,另外还需要制定对应的dll文件的path路径。




你可能感兴趣的:(IPCV,VC编程)