GDAL与OpenCV2.X数据转换(适合多光谱和高光谱等多通道的遥感影像)
一、前言
GDAL具有强大的图像读写功能,但是对常用图像处理算法的集成较少,OpenCV恰恰具有较强的图像处理能力,因此有效的结合两者对图像(遥感影像)的处理带来了极大的方便。那么如何实现GDAL与openCV间的数据交换成为影像处理中的关键步骤。接下来我将记录下:1 如何将GDAL读取的影像转化为openCV支持的的MAT格式?2 如何将处理后MAT数据转化为合适的图像格式存储?(PS:本人也是初次使用GDAL和openCV,代码很水。。。只是记录下自己学的,和大家交流下)
二、GDAL数据到openCV的MAT格式
关于GDAL数据到openCV的格式转化,网上已有部分资源,但是大多是针对单或者三通道的数据而言,对多通道图像(遥感的多光谱和高光谱影像)的转化不多,话不多说,先上代码:

- 1 cv::Mat GDAL2Mat(const QString fileName)
- 2 {
- 3 GDALAllRegister(); // 注册。。。
- 4 GDALDataset *poDataset = (GDALDataset *)GDALOpen(fileName.toStdString().c_str(),GA_ReadOnly);
- 5 int tmpCols = poDataset->GetRasterXSize();
- 6 int tmpRows = poDataset->GetRasterYSize();
- 7 int tmpBandSize = poDataset->GetRasterCount();
- 8 double *tmpadfGeoTransform = new double[6];
- 9 poDataset->GetGeoTransform(tmpadfGeoTransform);
- 10
- 11 QVector <cv::Mat> imgMat; // 每个波段
- 12 float *pafScan = new float[tmpCols*tmpRows]; // 存储数据
- 13
- 14 for(int i = 0;i< tmpBandSize;i++)
- 15 {
- 16 GDALRasterBand *pBand = poDataset->GetRasterBand(i+1);
- 17 //pafScan = new float[tmpCols*tmpRows];
- 18 pBand->RasterIO(GF_Read,0,0,tmpCols,tmpRows,pafScan,
- 19 tmpCols,tmpRows,GDT_Float32,0,0);
- 20 cv::Mat tmpMat = cv::Mat(tmpRows,tmpCols,CV_32FC1,pafScan);
- 21 imgMat.push_back(tmpMat.clone());
- 22 }
- 23 delete []pafScan;
- 24 pafScan = NULL;
- 25
- 26 cv::Mat img;
- 27 img.create(tmpRows,tmpCols,CV_32FC(tmpBandSize));
- 28 cv::merge(imgMat.toStdVector(),img);
- 29 imgMat.clear();
- 30 GDALClose((GDALDatasetH)poDataset);
- 31 return img;
- 32 }

思路就是:根据文件名获得其GDALDataset数据集,然后分波段(波段相当于通道)存储在格式为Vector<cv::Mat>的容器内,最后利用MAT的Merge函数,对通道数据进行组合。以上方法适合任意波段数据,对多通道影像,如遥感影像中多光谱和高光谱数据比较实用。但,存在一个问题:代码中红色部分,目的为释放poDataset的内存,但总会报错,注释后就没有问题了,不知道为啥,哪位大侠如果知道原因并且也恰巧路过此地,请给予帮助,谢谢!(问题解决了,GDALDataset数据集前不能释放其每个波段的指针,否则报错,代码已修改,下同)
三、MAT格式数据转化为GDAL数据集格式后并保存合适文件
思路是上面第二部分的逆过程。首先创建一个数据集和文件驱动,根据相关参数创建文件,并将多通道MAT数据通过CV::split函数进行通道分离,最后将通道数据与GDAL数据集的波段数据对应,一一写入数据集中。代码如下:

- 1 bool Mat2File(cv::Mat img, const QString fileName)
- 2 {
- 3 if(img.empty()) // 判断是否为空
- 4 return 0;
- 5
- 6 const int nBandCount=img.channels();
- 7 const int nImgSizeX=img.cols;
- 8 const int nImgSizeY=img.rows;
- 9
- 10 // 将通道分开
- 11 // imgMat每个通道数据连续
- 12 std::vector<cv::Mat> imgMat(nBandCount);
- 13 cv::split(img,imgMat);
- 14
- 15 // 分波段写入文件
- 16 GDALAllRegister();
- 17 GDALDataset *poDataset; //GDAL数据集
- 18 GDALDriver *poDriver; //驱动,用于创建新的文件
- 19 poDriver = GetGDALDriverManager()->GetDriverByName("ENVI");
- 20
- 21 if(poDriver == NULL)
- 22 return 0;
- 23 poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
- 24 GDT_Float32,NULL);
- 25 // 循环写入文件
- 26 GDALRasterBand *pBand = NULL;
- 27 float *ppafScan = new float[nImgSizeX*nImgSizeY];
- 28 cv::Mat tmpMat;// = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1);
- 29
- 30 int n1 = nImgSizeY;
- 31 int nc = nImgSizeX;
- 32
- 33 for(int i = 1;i<=nBandCount;i++)
- 34 {
- 35 pBand = poDataset->GetRasterBand(i);
- 36 tmpMat = imgMat.at(i-1);
- 37 if(tmpMat.isContinuous())
- 38 {
- 39 nc = n1*nc;
- 40 n1 = 1;
- 41 }
- 42 for(int r = 0;r<n1;r++)
- 43 {
- 44 int tmpI = r*nImgSizeX;
- 45 float *p = tmpMat.ptr<float>(r);
- 46 for(int c = 0;c<nc;c++)
- 47 {
- 48 ppafScan[tmpI+c] = p[c];
- 49 }
- 50 }
- 51 pBand->RasterIO(GF_Write,0,0,nImgSizeX,nImgSizeY,ppafScan,
- 52 nImgSizeX,nImgSizeY,GDT_Float32,0,0);
- 53 }
- 54 delete []ppafScan;
- 55 ppafScan = NULL;
- 56 GDALClose(poDataset);
- 57 return 1;
- 58 }


- 60 bool ChooseSample::Mat2File(std::vector<cv::Mat> imgMat, const QString fileName)
- 61 {
- 62 if(imgMat.empty()) // 判断是否为空
- 63 {
- 64 QMessageBox::information(this,"Message Error","Data NULL!");
- 65 return 0;
- 66 }
- 67
- 68 const int nBandCount=imgMat.size();
- 69 const int nImgSizeX=imgMat[0].cols;
- 70 const int nImgSizeY=imgMat[0].rows;
- 71
- 72 // 分波段写入文件
- 73 GDALAllRegister();
- 74 GDALDataset *poDataset; //GDAL数据集
- 75 GDALDriver *poDriver; //驱动,用于创建新的文件
- 76 poDriver = GetGDALDriverManager()->GetDriverByName("ENVI");
- 77
- 78 if(poDriver == NULL)
- 79 return 0;
- 80 poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
- 81 GDT_Float32,NULL);
- 82 // 循环写入文件
- 83 GDALRasterBand *pBand = NULL;
- 84 float *ppafScan = new float[nImgSizeX*nImgSizeY];
- 85 cv::Mat tmpMat;// = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1);
- 86
- 87 int n1 = nImgSizeY;
- 88 int nc = nImgSizeX;
- 89
- 90 for(int i = 1;i<=nBandCount;i++)
- 91 {
- 92 pBand = poDataset->GetRasterBand(i);
- 93 tmpMat = imgMat.at(i-1);
- 94 if(tmpMat.isContinuous())
- 95 {
- 96 nc = n1*nc;
- 97 n1 = 1;
- 98 }
- 99 for(int r = 0;r<n1;r++)
- 100 {
- 101 int tmpI = r*nImgSizeX;
- 102 float *p = tmpMat.ptr<float>(r);
- 103 for(int c = 0;c<nc;c++)
- 104 {
- 105 ppafScan[tmpI+c] = p[c];
- 106 }
- 107 }
- 108 pBand->RasterIO(GF_Write,0,0,nImgSizeX,nImgSizeY,ppafScan,
- 109 nImgSizeX,nImgSizeY,GDT_Float32,0,0);
- 110 }
- 111 delete []ppafScan;
- 112 ppafScan = NULL;
- 113 GDALClose(poDataset);
- 114 return 1;
- 115 }

同样有如上的困扰,每当释放内存就会报错(代码中红色字体处)。此外,关于cv::split函数有一个小的细节问题,如下:

- 1 // 将通道分开
- 2 // imgMat每个通道数据连续
- 3 std::vector<cv::Mat> imgMat(nBandCount);
- 4 cv::split(img,imgMat);
- 5
- 6 // imgMat每个通道数据不连续
- 7 QVector<cv::Mat> imgMat(nBandCount);
- 8 cv::split(img,imgMat.toStdVector());

GDAL与OpenCV2.X数据转换(适合多光谱和高光谱等多通道的遥感影像)的更多相关文章
- 1. GDAL与OpenCV2.X数据转换(适合多光谱和高光谱等多通道的遥感影像)
一.前言 GDAL具有强大的图像读写功能,但是对常用图像处理算法的集成较少,OpenCV恰恰具有较强的图像处理能力,因此有效的结合两者对图像(遥感影像)的处理带来了极大的方便.那么如何实现GDAL与o ...
- 基于GDAL的遥感影像显示(C#版)
基于GDAL的遥感影像显示(C#版) - 菜菜的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/RSyaoxin/article/details/9220735
- 【遥感影像】Python GDAL 像素与坐标对应
转:https://blog.csdn.net/theonegis/article/details/50805520 https://blog.csdn.net/wsp_1138886114/arti ...
- gdal 遥感影像水体数据提取
- GDAL多光谱与全色图像融合简单使用
目录 简述 C++代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 ...
- AE + GDAL实现影像按标准图幅分割(上)
最近有个项目,其中有个功能是要将遥感影像按标准图幅分割,一开始用AE的接口,慢的让人抓狂,就改用GDAL,速度提升很大.我主要通过http://blog.csdn.net/liminlu0314/学习 ...
- 使用GDAL进行RPC坐标转换
使用GDAL进行RPC坐标转换 对于高分辨率遥感卫星数据而言,目前几乎都提供了有理函数模型(RFM)来进行图像校正(SPOT系列提供了有理函数模型之外还提供了严格轨道模型).对遥感影像进行校正目前最常 ...
- GDAL指定自定义的金字塔目录
缘起 对于一般的遥感影像文件,金字塔文件默认都是与影像文件放在同一个目录下,金字塔文件名一般与源影像文件名相同,但后缀名不同.或者金字塔内建于影像内部,但这不是这里所涉及的. 在使用ArcGIS桌面版 ...
- 基于GDAL的栅格图像空间插值预处理
转自 基于GDAL的栅格图像空间插值预处理——C语言版 基于GDAL的栅格图像预处理 前言 栅格数据和矢量数据构成空间数据的主要来源,怎样以开源方式读取并处理这些空间数据?目前有多种开源支持包,这里只 ...
随机推荐
- Spring3.0.5 获取表中自增的主键(mysql)
public int addWsstxCotent(final WsstxContent wsstxContent) { final String sql = "insert into ws ...
- 系统service
Context.TELEPHONY_SERVICE TelephonyManager tManager = (TelephonyManager)getSystemService(Context.TEL ...
- HttpWebResponse类
HttpWebResponse类的作用用于在客户端获取返回的响应的信息,还记得HttpResponse类吗?你是否在写B/S程序的时候,经常用到Response.Write()呢? HttpRespo ...
- Linux打开windows 的txt文件,乱码的问题
实际是两种不同操作系统对中文编码的不一样.转换下就行了 references: http://www.cnblogs.com/no7dw/archive/2013/05/21/3090594.html
- Linux系统维护修复模式
基于PXE方式的Linux系统维护工具箱 在安装RedHat Linux系统的过程中,我们知道可以通过PXE方式进行安装,从而解决了无光驱或无安装介质(光盘)来安装操作系统.但是当系统由于某种 ...
- Unix/Linux环境C编程入门教程(37) shell常用命令演练
cat命令 cat命令可以用来查看文件内容. cat [参数] 文件名. grep-指定文件中搜索指定字符内容. Linux的目录或文件. -path '字串' 查找路径名匹配所给字串的所有文件 ...
- House Robber II 解答
Question After robbing those houses on that street, the thief has found himself a new place for his ...
- [Ruby学习总结]Ruby中的类
1.类名的定义以大写字母开头,单词首字母大写,不用"_"分隔 2.实例化对象的时候调用new方法,实际上调用的是类里边的initialize方法,是ruby类的初始化方法,功能等同 ...
- 【转】【漫画解读】HDFS存储原理
根据Maneesh Varshney的漫画改编,以简洁易懂的漫画形式讲解HDFS存储机制与运行原理. 一.角色出演 如上图所示,HDFS存储相关角色与功能如下: Client:客户端,系统使用者,调用 ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...