一、前言
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); // GDAL數據集
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; // 存儲數據
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 delete []pBand;
23 tmpMat.release();
24 }
25 delete []pafScan;
26 cv::Mat img;
27 img.create(tmpRows,tmpCols,CV_32FC(tmpBandSize));
28 cv::merge(imgMat.toStdVector(),img);
29 //GDALClose((GDALDatasetH)poDataset);
30 imgMat.clear();
31 return img;
32 }
思路就是:根據文件名獲得其GDALDataset數據集,然後分波段(波段相當於通道)存儲在格式為Vector<cv::Mat>的容器內,最後利用MAT的Merge函數,對通道數據進行組合。以上方法適合任意波段數據,對多通道影像,如遙感影像中多光譜和高光譜數據比較實用。但,存在一個問題:代碼中紅色部分,目的為釋放poDataset的內存,但總會報錯,注釋後就沒有問題了,不知道為啥,哪位大俠如果知道原因並且也恰巧路過此地,請給予幫助,謝謝!
三、MAT格式數據轉化為GDAL數據集格式後並保存合適文件
思路是上面第二部分的逆過程。首先創建一個數據集和文件驅動,根據相關參數創建文件,並將多通道MAT數據通過CV::split函數進行通道分離,最後將通道數據與GDAL數據集的波段數據對應,一一寫入數據集中。代碼如下:
1 bool Mat2File(std::vector<cv::Mat> imgMat, const QString fileName)
2 {
3 if(imgMat.empty()) // 判斷是否為空
4 {
5 QMessageBox::information(this,"Message Error","Data NULL!");
6 return 0;
7 }
8
9 const int nBandCount=imgMat.size();
10 const int nImgSizeX=imgMat[0].cols;
11 const int nImgSizeY=imgMat[0].rows;
12
13 // 分波段寫入文件
14 GDALAllRegister();
15 GDALDataset *poDataset; //GDAL數據集
16 GDALDriver *poDriver; //驅動,用於創建新的文件
17 poDriver = GetGDALDriverManager()->GetDriverByName("ENVI");
18
19 if(poDriver == NULL)
20 return 0;
21 poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
22 GDT_Float32,NULL);
23 // 循環寫入文件
24 GDALRasterBand *pBand = NULL;
25 float *ppafScan;
26 for(int i = 1;i<=nBandCount;i++)
27 {
28 pBand = poDataset->GetRasterBand(i);
29 cv::Mat tmpMat = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1);
30 tmpMat = imgMat.at(i-1).clone();
31 ppafScan = new float[nImgSizeX*nImgSizeY];
32 if(tmpMat.isContinuous())
33 {
34 ppafScan = tmpMat.ptr<float>(0);
35 }else
36 {
37 for(int r = 0;r<nImgSizeY;r++)
38 {
39 int tmpI = r*nImgSizeX;
40 float *p = tmpMat.ptr<float>(r);
41 for(int c = 0;c<nImgSizeX;c++)
42 {
43 ppafScan[tmpI+c] = p[c];
44 }
45 }
46 }
47 pBand->RasterIO(GF_Write,0,0,nImgSizeX,nImgSizeY,ppafScan,
48 nImgSizeX,nImgSizeY,GDT_Float32,0,0);
49 tmpMat.release();
50 }
51 delete pBand;
52 delete poDriver;
53 //delete ppafScan;
54 //delete poDataset;
55 return 1;
56 }
1 bool ChooseSample::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 std::vector<cv::Mat> imgMat(nBandCount);
12 cv::split(img,imgMat);
13
14 // 分波段寫入文件
15 GDALAllRegister();
16 GDALDataset *poDataset; //GDAL數據集
17 GDALDriver *poDriver; //驅動,用於創建新的文件
18 poDriver = GetGDALDriverManager()->GetDriverByName("ENVI");
19
20 if(poDriver == NULL)
21 return 0;
22 poDataset=poDriver->Create(fileName.toStdString().c_str(),nImgSizeX,nImgSizeY,nBandCount,
23 GDT_Float32,NULL);
24 // 循環寫入文件
25 GDALRasterBand *pBand = NULL;
26 float *ppafScan;
27 for(int i = 1;i<=nBandCount;i++)
28 {
29 pBand = poDataset->GetRasterBand(i);
30 cv::Mat tmpMat = cv::Mat(nImgSizeY,nImgSizeX,CV_32FC1);
31 tmpMat = imgMat.at(i-1).clone();
32 ppafScan = new float[nImgSizeX*nImgSizeY];
33 if(tmpMat.isContinuous())
34 {
35 ppafScan = tmpMat.ptr<float>(0);
36 }else
37 {
38 for(int r = 0;r<nImgSizeY;r++)
39 {
40 int tmpI = r*nImgSizeX;
41 float *p = tmpMat.ptr<float>(r);
42 for(int c = 0;c<nImgSizeX;c++)
43 {
44 ppafScan[tmpI+c] = p[c];
45 }
46 }
47 }
48 pBand->RasterIO(GF_Write,0,0,nImgSizeX,nImgSizeY,ppafScan,
49 nImgSizeX,nImgSizeY,GDT_Float32,0,0);
50 tmpMat.release();
51 }
52 delete pBand;
53 delete poDriver;
54 //delete ppafScan;
55 //delete poDataset;
56 return 1;
57 }
同樣有如上的困擾,每當釋放內存就會報錯(代碼中紅色字體處)。此外,關於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());