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());
复制代码
原文地址:https://www.cnblogs.com/lovebay/p/5335442.html