• 1. 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());
  • 相关阅读:
    原型模板分享——国外高阶版“大众点评”TripAdvisor
    插画手绘入门教程
    十大用户体验设计失败案例,你知道吗?
    用摹客,轻松管理设计规范!
    摹客“独门绝技”之评论审阅篇
    政务管理系统原型模板分享
    产品经理如何做需求分析?
    python基础知socket编程
    异常处理
    元类,__call__方法和单例模式
  • 原文地址:https://www.cnblogs.com/zyore2013/p/4657702.html
Copyright © 2020-2023  润新知