一、前言
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());
