一、前言

GDAL具有强大的图像读写功能,但是对常用图像处理算法的集成较少,OpenCV恰恰具有较强的图像处理能力,因此有效的结合两者对图像(遥感影像)的处理带来了极大的方便。那么如何实现GDAL与openCV间的数据交换成为影像处理中的关键步骤。接下来我将记录下:1 如何将GDAL读取的影像转化为openCV支持的的MAT格式?2 如何将处理后MAT数据转化为合适的图像格式存储?(PS:本人也是初次使用GDAL和openCV,代码很水。。。只是记录下自己学的,和大家交流下)

二、GDAL数据到openCV的MAT格式

关于GDAL数据到openCV的格式转化,网上已有部分资源,但是大多是针对单或者三通道的数据而言,对多通道图像(遥感的多光谱和高光谱影像)的转化不多,话不多说,先上代码:

 cv::Mat GDAL2Mat(const QString fileName)
{
GDALAllRegister(); // 注册。。。
GDALDataset *poDataset = (GDALDataset *)GDALOpen(fileName.toStdString().c_str(),GA_ReadOnly);
int tmpCols = poDataset->GetRasterXSize();
int tmpRows = poDataset->GetRasterYSize();
int tmpBandSize = poDataset->GetRasterCount();
double *tmpadfGeoTransform = new double[];
poDataset->GetGeoTransform(tmpadfGeoTransform); QVector <cv::Mat> imgMat; // 每个波段
float *pafScan = new float[tmpCols*tmpRows]; // 存储数据 for(int i = ;i< tmpBandSize;i++)
{
GDALRasterBand *pBand = poDataset->GetRasterBand(i+);
//pafScan = new float[tmpCols*tmpRows];
pBand->RasterIO(GF_Read,,,tmpCols,tmpRows,pafScan,
tmpCols,tmpRows,GDT_Float32,,);
cv::Mat tmpMat = cv::Mat(tmpRows,tmpCols,CV_32FC1,pafScan);
imgMat.push_back(tmpMat.clone());
}
delete []pafScan;
pafScan = NULL; cv::Mat img;
img.create(tmpRows,tmpCols,CV_32FC(tmpBandSize));
cv::merge(imgMat.toStdVector(),img);
imgMat.clear();
GDALClose((GDALDatasetH)poDataset);
return img;
}

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

三、MAT格式数据转化为GDAL数据集格式后并保存合适文件

思路是上面第二部分的逆过程。首先创建一个数据集和文件驱动,根据相关参数创建文件,并将多通道MAT数据通过CV::split函数进行通道分离,最后将通道数据与GDAL数据集的波段数据对应,一一写入数据集中。代码如下:

 bool Mat2File(cv::Mat img, const QString fileName)
{
if(img.empty()) // 判断是否为空
return ; const int nBandCount=img.channels();
const int nImgSizeX=img.cols;
const int nImgSizeY=img.rows; // 将通道分开
// imgMat每个通道数据连续
std::vector<cv::Mat> imgMat(nBandCount);
cv::split(img,imgMat); // 分波段写入文件
GDALAllRegister();
GDALDataset *poDataset; //GDAL数据集
GDALDriver *poDriver; //驱动,用于创建新的文件
poDriver = GetGDALDriverManager()->GetDriverByName("ENVI"); if(poDriver == NULL)
return ;
poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
GDT_Float32,NULL);
// 循环写入文件
GDALRasterBand *pBand = NULL;
float *ppafScan = new float[nImgSizeX*nImgSizeY];
cv::Mat tmpMat;// = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1); int n1 = nImgSizeY;
int nc = nImgSizeX; for(int i = ;i<=nBandCount;i++)
{
pBand = poDataset->GetRasterBand(i);
tmpMat = imgMat.at(i-);
if(tmpMat.isContinuous())
{
nc = n1*nc;
n1 = ;
}
for(int r = ;r<n1;r++)
{
int tmpI = r*nImgSizeX;
float *p = tmpMat.ptr<float>(r);
for(int c = ;c<nc;c++)
{
ppafScan[tmpI+c] = p[c];
}
}
pBand->RasterIO(GF_Write,,,nImgSizeX,nImgSizeY,ppafScan,
nImgSizeX,nImgSizeY,GDT_Float32,,);
}
delete []ppafScan;
ppafScan = NULL;
GDALClose(poDataset);
return ;
}
 bool ChooseSample::Mat2File(std::vector<cv::Mat> imgMat, const QString fileName)
{
if(imgMat.empty()) // 判断是否为空
{
QMessageBox::information(this,"Message Error","Data NULL!");
return ;
} const int nBandCount=imgMat.size();
const int nImgSizeX=imgMat[].cols;
const int nImgSizeY=imgMat[].rows; // 分波段写入文件
GDALAllRegister();
GDALDataset *poDataset; //GDAL数据集
GDALDriver *poDriver; //驱动,用于创建新的文件
poDriver = GetGDALDriverManager()->GetDriverByName("ENVI"); if(poDriver == NULL)
return ;
poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
GDT_Float32,NULL);
// 循环写入文件
GDALRasterBand *pBand = NULL;
float *ppafScan = new float[nImgSizeX*nImgSizeY];
cv::Mat tmpMat;// = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1); int n1 = nImgSizeY;
int nc = nImgSizeX; for(int i = ;i<=nBandCount;i++)
{
pBand = poDataset->GetRasterBand(i);
tmpMat = imgMat.at(i-);
if(tmpMat.isContinuous())
{
nc = n1*nc;
n1 = ;
}
for(int r = ;r<n1;r++)
{
int tmpI = r*nImgSizeX;
float *p = tmpMat.ptr<float>(r);
for(int c = ;c<nc;c++)
{
ppafScan[tmpI+c] = p[c];
}
}
pBand->RasterIO(GF_Write,,,nImgSizeX,nImgSizeY,ppafScan,
nImgSizeX,nImgSizeY,GDT_Float32,,);
}
delete []ppafScan;
ppafScan = NULL;
GDALClose(poDataset);
return ;
}

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

     //    将通道分开
// imgMat每个通道数据连续
std::vector<cv::Mat> imgMat(nBandCount);
cv::split(img,imgMat); // imgMat每个通道数据不连续
QVector<cv::Mat> imgMat(nBandCount);
cv::split(img,imgMat.toStdVector());

1. GDAL与OpenCV2.X数据转换(适合多光谱和高光谱等多通道的遥感影像)的更多相关文章

  1. GDAL与OpenCV2.X数据转换(适合多光谱和高光谱等多通道的遥感影像)

    一.前言 GDAL具有强大的图像读写功能,但是对常用图像处理算法的集成较少,OpenCV恰恰具有较强的图像处理能力,因此有效的结合两者对图像(遥感影像)的处理带来了极大的方便.那么如何实现GDAL与o ...

  2. 基于GDAL的遥感影像显示(C#版)

    基于GDAL的遥感影像显示(C#版) - 菜菜的专栏 - 博客频道 - CSDN.NET  http://blog.csdn.net/RSyaoxin/article/details/9220735

  3. 【遥感影像】Python GDAL 像素与坐标对应

    转:https://blog.csdn.net/theonegis/article/details/50805520 https://blog.csdn.net/wsp_1138886114/arti ...

  4. gdal 遥感影像水体数据提取

  5. GDAL多光谱与全色图像融合简单使用

    目录 简述 C++代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 ...

  6. AE + GDAL实现影像按标准图幅分割(上)

    最近有个项目,其中有个功能是要将遥感影像按标准图幅分割,一开始用AE的接口,慢的让人抓狂,就改用GDAL,速度提升很大.我主要通过http://blog.csdn.net/liminlu0314/学习 ...

  7. 使用GDAL进行RPC坐标转换

    使用GDAL进行RPC坐标转换 对于高分辨率遥感卫星数据而言,目前几乎都提供了有理函数模型(RFM)来进行图像校正(SPOT系列提供了有理函数模型之外还提供了严格轨道模型).对遥感影像进行校正目前最常 ...

  8. GDAL指定自定义的金字塔目录

    缘起 对于一般的遥感影像文件,金字塔文件默认都是与影像文件放在同一个目录下,金字塔文件名一般与源影像文件名相同,但后缀名不同.或者金字塔内建于影像内部,但这不是这里所涉及的. 在使用ArcGIS桌面版 ...

  9. 基于GDAL的栅格图像空间插值预处理

    转自 基于GDAL的栅格图像空间插值预处理——C语言版 基于GDAL的栅格图像预处理 前言 栅格数据和矢量数据构成空间数据的主要来源,怎样以开源方式读取并处理这些空间数据?目前有多种开源支持包,这里只 ...

随机推荐

  1. Android调试系列—使用android studio调试smali代码

    1.工具介绍 使用工具 android killer:用于反编译apk包,得到smali代码 android studio:调试smali代码工具,或者使用idea,android studio就是在 ...

  2. C# KeyValuePair<TKey,TValue>与Container

    KeyValuePair<TKey,TValue>  KeyValuePair<TKey,TValue>是一个结构体,相当于C#一个Map类型的对象,可以通过它记录一个键/值对 ...

  3. Asp.net中实现同一用户名同时登陆,注销先前用户(转)

    Web 项目中经常遇到的问题就是同一用户名多次登陆的问题,相应的解决办法也很多,总结起来不外乎这几种解决办法:将登陆后的用户名放到数据库表中:登陆后的用 户名放到Session中:登陆后的用户名放到A ...

  4. Silverlight visifire Chart图表下载到PPT文件中

    一.Silverlight xaml.cs文件 1. //下载图表        private void btnDown_Click(object sender, RoutedEventArgs e ...

  5. Tornado模块分类和各模块之间的关系

    1. Core web framework tornado.web — 包含web框架的大部分主要功能,包含RequestHandler和Application两个重要的类 tornado.https ...

  6. javascript第二课练习

    <script type="text/javascript">   window.onload=function()   //网页全部加载完后执行   {     va ...

  7. BZOJ 1877 晨跑

    http://www.lydsy.com/JudgeOnline/problem.php?id=1877 思路:拆点费用流,答案就是最大流量和最小费用. #include<algorithm&g ...

  8. 《Programming WPF》翻译 第7章 4.转换

    原文:<Programming WPF>翻译 第7章 4.转换 支持高分辨率显示是WPF中的重要样式.这是被部分地支持--强调了可伸缩的向量图,而不是图像.但是,正如使用GDI+和GDI3 ...

  9. Android 有缓存功能的请求封装接口

    /* * @Company 浙 江 鸿 程 计 算 机 系 统 有 限 公 司 * @URL http://www.zjhcsoft.com * @Address 杭州滨江区伟业路1号 * @Emai ...

  10. JAVASCRIPT实现XML分页

    为了方便用户查看大批量数据,我们会用到动态分页,因此分页功能是我们在网站上见过的最普遍也是最常用的一个功能模块了.而以往的信息分页都是连接到数据库的,每一次点击都必须要后台数据库的支持.这样不但服务器 ...