基于GDAL库图像读写——涉及(tif/tiff/bmp/jpg/png/gif等)多种格式图像的I/O

Posted nanke_yh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于GDAL库图像读写——涉及(tif/tiff/bmp/jpg/png/gif等)多种格式图像的I/O相关的知识,希望对你有一定的参考价值。

        说来也是神奇,之所以会发这篇文章,还是因为在CSDN发文章中无法上传tif格式的图片造成的,如下。

         转入文件夹下,并不显示tif图片,选择所有文件确认一副tiff图片,仍然提示上传错误。不知道CSDN开发人员为什么要做这样的限制,只想问一下有啥意义呢?

        由于以前在图像处理过程中一直使用的GDAL读写图片接口都很好用,也就都没细究,每次输出图片路径带上tif拓展名,就都没啥事。这次不是限制了嘛,想着输出图像直接改成其他格式不就行了。直接保存的图片拓展名改成jpg等,发现保存出来的图片无法打开,图片查看失败,提示文件损坏。

        于是让我重新研究了一下GDAL读写图像的函数接口,其中主要用到的是RasterIO()函数,这个可参考李民录老师的博文:

GDAL源码剖析(七)之GDAL RasterIO使用说明(续)_GDAL专栏-CSDN博客

GDAL源码剖析(七)之GDAL RasterIO使用说明_GDAL专栏-CSDN博客_gdal rasterio

        这里都以灰度图像(单波段)读写函数来演示。

        对于读函数图像的格式影像不大,都能直接读入到内存:

ImgTyp* OpenImage(const char *FilePath,int &width,int &height )
											
	GDALAllRegister();//注册、读取图像
	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");//使之支持中文路径
	GDALDataset *poDataset = NULL;
	poDataset = (GDALDataset*)GDALOpen(FilePath,GA_ReadOnly);
	if(poDataset == NULL)
	
		cout<<"无法打开影像!"<<endl;
		GDALDestroyDriverManager();
	
	//获取图像数据的参数
	width = poDataset->GetRasterXSize();
	height = poDataset->GetRasterYSize();
	//int nRastercount = poDataset->GetRasterCount();
	//开辟内存
	ImgTyp *pImageData = new ImgTyp[width * height];
	int bandList = 1;
	poDataset->RasterIO(GF_Read,0,0,width,height,pImageData,width,height,(GDALDataType)sizeof(ImgTyp)/*GDT_Byte*/,1,&bandList,0,0,0);
	//cout<<"单波段影像读入完成!"<<endl;
	//关闭GDAL库相关驱动和释放内存
	GDALClose(poDataset);
	return pImageData;

对于写函数,原函数:

void WriteTif( ImgTyp* pImageData,char *savepath,int width,int height)  
  
	GDALAllRegister();
	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");//使之支持中文路径
	char *GType = NULL;
	GType = findImageTypeGDAL(savepath);
	// 创建结果存储图像  
	if ( GType == NULL )  
	  
		cout << "没有指定存储图像的格式,默认为GTiff" << endl;  
	  
	const char *format ="GTiff";
	GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName( format ); 
	GDALDataset* WriteDataSet = poDriver->Create( savepath, width, height, 1, (GDALDataType)sizeof(ImgTyp)/*GDT_Byte*/, NULL );
	GDALRasterBand* pand = WriteDataSet->GetRasterBand(1);  
	pand->RasterIO( GF_Write, 0, 0, width, height, pImageData, width, height, (GDALDataType)sizeof(ImgTyp)/*GDT_Byte*/, 0, 0 ); 
	GDALClose( WriteDataSet );  
	cout<<"单波段影像保存完成!"<<endl;

其中findImageTypeGDAL函数:

char* findImageTypeGDAL(char *pImgFileName)

	char *Gtype = NULL;
	char fileExtension[_MAX_PATH]=NULL;
	strcpy(fileExtension,pImgFileName);
	if      ((strstr(fileExtension,".tif")!=NULL))  Gtype = "GTiff";
	else if ((strstr(fileExtension,".tiff")!=NULL)) Gtype = "GTiff";
	else if ((strstr(fileExtension,".jpg")!=NULL))  Gtype = "JPEG";
	else if ((strstr(fileExtension,".bmp")!=NULL))  Gtype = "BMP";
	else if ((strstr(fileExtension,".png")!=NULL))  Gtype = "PNG";  
	else if ((strstr(fileExtension,".gif")!=NULL))  Gtype = "GIF";
	else Gtype = NULL;
	//else Gtype = "GDALRaw";
	return Gtype;

        我们重新来review一下上面两个函数,看findImageTypeGDAL函数,是不是发现能够解析各种格式的图片是吧。然后看WriteTif函数,内部首先根据路径获得保存图片的拓展名GType ,判断之后发现重新定义了const char *format ="GTiff";并且后面图片保存方式也都是按照GTiff格式来的:GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName( format );与GType无关了。

        那么问题来了,对于保存为jpg等类型的情况下,解析GType为“JPEG”,但还是直接按tiff格式输出,最后文件名直接拓展名为.jpg而已。如此,该图片保存出来就会出现文件损坏问题。

        主要是因为:利用GDAL库函数创建图像时,一般会用到GDALDriver类Create()函数,但是Create()函数不支持JPEG、PNG等格式。

        所以又要怎么去保存JPEG、PNG等格式呢?,虽然Create()函数不支持但是CreateCopy()支持这些格式,所以根据已有的图像数据,不能直接创建jpg、png格式的图像,而要借助GDAL的MEM内存文件,来创建他们。如是就有了下一面改写的全格式适应的图片写函数:

void WriteOutImg( ImgTyp* pImageData,char *savepath,int width,int height)  
  
	GDALAllRegister();
	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");//使之支持中文路径
	char *GType = NULL;
	GType = findImageTypeGDAL(savepath);
	GDALDriver *poDriver;
	GDALDataset* WriteDataSet;
	GDALRasterBand* pand;
	CPLErr err;
	if ( GType == NULL ) 
	  
		cout << "没有指定存储图像的格式,默认为GTiff" << endl; 
		GType = "GTiff";
	
	if ( strcmp("BMP",GType) == 0  ||  strcmp("GTiff",GType) == 0) 
	  
		poDriver = GetGDALDriverManager()->GetDriverByName( GType ); 
		// 创建结果存储图像
		WriteDataSet = poDriver->Create( savepath, width, height, 1, (GDALDataType)sizeof(ImgTyp)/*GDT_Byte*/, NULL );
		pand = WriteDataSet->GetRasterBand(1);  
		err = pand->RasterIO( GF_Write, 0, 0, width, height, pImageData, width, height, (GDALDataType)sizeof(ImgTyp)/*GDT_Byte*/, 0, 0 ); 
		if(err != CE_None)
		
			printf("写图像数据失败!");
			return ;
		
	  
	else
	
		poDriver = GetGDALDriverManager()->GetDriverByName("MEM");
		WriteDataSet = poDriver->Create("",width, height,1,(GDALDataType)sizeof(ImgTyp),NULL);
		pand = WriteDataSet->GetRasterBand(1);
		err = pand->RasterIO(GF_Write,0,0,width, height,pImageData,width, height,(GDALDataType)sizeof(ImgTyp),0, 0);
		if(err != CE_None)
		
			printf("写图像数据失败!");
			return ;
		
		//以创建复制的方式,生成jpg文件
		GDALDriver *pDriverJPG = GetGDALDriverManager()->GetDriverByName(GType);
		pDriverJPG->CreateCopy(savepath,WriteDataSet,TRUE,0,0,0); //创建jpg文件
	
	GDALClose( WriteDataSet );
	cout<<"单波段影像保存完成!"<<endl;

 其中涉及的字符串比较呢,可参考

C语言中字符串之间的比较【char*】/【string】_nanke_yh的博客-CSDN博客

而上面借助GDAL的MEM内存文件来保存jpg,png等图片可参考:GDAL创建JPG格式图像_hong__fang的专栏-CSDN博客

最后通过上面的I/O接口,对spot.tif影像进行测试,分别输出不同格式的影像:

         发现所有影像均保存正常,文件显示正常,且保存影像的色彩正常。如此基于GDAL多中格式的图像读写接口就完善了,后面做图像处理就可以放心使用这两个接口了。

以上是关于基于GDAL库图像读写——涉及(tif/tiff/bmp/jpg/png/gif等)多种格式图像的I/O的主要内容,如果未能解决你的问题,请参考以下文章

基于Python GDAL库实现图像的几何校正详细教程

gdal学习笔记利用python 的gdal,以及相关库进行遥感图像处理(影像裁剪,辐射定标,大气校正,异常值去除)——以基于landsat8数据提取NDVI为例

C#中GDAL读写shp图层

使用gdal库读取图像

GDAL库——读取图像并提取基本信息

改动GDAL库支持RPC像方改正模型