GDAL 数据读取(float型)与 OpenCV数据读取的等价
开发环境:VS2015+QT5.7.0
工程已配置好GDAL与OpenCV
在遥感影像数据处理中,我们常常要利用GDAL强大的对各类数据的支持能力读取数据,然后利用OpenCV丰富的算法库处理数据。
在做项目编码的过程中,我碰到一个需要把GDAL的数据转成MAT数据的问题。GDAL读取数据时数据的组织方式是RRR…GGG…BBB…,而OpenCV的Mat组织数据的方式是BGRBGRBGR…要实现这样的互转很容易,利用OpenCV的split()函数可以实现。示例如下:
这里写代码片
//GDAL//支持中文路径
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
//QString 转 const char*
QString m_strPath = "文件路径";
QByteArray ba = m_strPath.toLocal8Bit();
const char* ch = ba.data();
//GDAL数据集
GDALDataset *m_pDataset = (GDALDataset *)GDALOpen(ch, GA_ReadOnly);
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
if (m_pDataset == nullptr)
{
QMessageBox::information(this, tr("error"), tr("open file failed!!!"));
return ;
}
int bandCount = m_pDataset->GetRasterCount();//波段的数目
int width = m_pDataset->GetRasterXSize();
int height = m_pDataset->GetRasterYSize();
float* buf = new float[pIM->Width*pIM->Height];
GDALRasterBand *m_pBand = NULL;
for (d = 0; d < bandCount; ++d)
{
m_pBand = m_pDataset->GetRasterBand(d + 1);
if (m_pBand)
{
//读取数据,buf存储一个波段数据
if (CE_None == m_pBand->RasterIO(GF_Read, 0, 0, width, height, buf, width, height, GDT_Float32, 0, 0))
{
//数据处理
}
}
}
delete[] buf;
//OpenCV
Mat pSrcMat = imread(ch, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
if (!pSrcMat.data)
{
QMessageBox::information(this, tr("error"), tr("open file failed!!!"));
return;
}
int bandCount = pSrcMat.channels();
int width = pSrcMat.cols;
int height = pSrcMat.rows;
//!将通道分开
//!imgMat每个通道数据连续
std::vector<cv::Mat> bgrMat(bandCount);
cv::split(pSrcMat, bgrMat);
float* buf = new float[pIM->Width*pIM->Height];
for(int d = 0; d<bandCount; ++d)
{
buf = (float* )bgrMat[2-d].data;
//数据处理
}
如果要处理的数据类型是uchar,那这样转换应该就可以了。但是这里我要处理的是float型数据,由于OpenCV的imread函数总是把数据读成uchar型,这样的转换就会出问题。这时候需要加上如下代码:在用split()函数将通道分开后面,
这里写代码片 //格式转换
std::vector<cv::Mat> matOut;
QVector<double> _bandMaxS;
QVector<double> _bandMinS;
for (int i(0); i < bgrMat.size(); i++)
{
double max = _bandMaxS[i];
double min = _bandMinS[i];
cv::Mat cur = bgrMat[i];
makeNormal(GDT_Float32, cur, max, min);
matOut.push_back(cur);
}
其中的makeNormal()函数定义在这个源文件(.cpp)的头文件(.h)中(priviate), 函数内容如下:
这里写代码片 void makeNormal(GDALDataType type, cv::Mat& cur, double max, double min) const
{
switch (type)
{
case GDT_Byte:
this->normalize(cur, cur, 0, UCHAR_MAX, CV_8U, max, min);
break;
case GDT_UInt16:
this->normalize(cur, cur, 0, UINT16_MAX, CV_16U, max, min);
break;
case GDT_Int16:
this->normalize(cur, cur, INT16_MIN, INT16_MAX, CV_16S, max, min);
break;
case GDT_UInt32:
this->normalize(cur, cur, 0, UINT32_MAX, CV_32F, max, min);
break;
case GDT_Int32:
this->normalize(cur, cur, INT32_MIN, INT32_MAX, CV_32S, max, min);
break;
case GDT_Float32:
cur.convertTo(cur, CV_32F);
break;
case GDT_Float64:
cur.convertTo(cur, CV_64F);
break;
case GDT_CInt16: break;
case GDT_CInt32: break;
case GDT_CFloat32: break;
case GDT_CFloat64: break;
case GDT_TypeCount: break;
case GDT_Unknown: break;
default: break;
}
}
normalize()定义在头文件的public:
这里写代码片 static void normalize(const cv::Mat& _src, cv::Mat& _dst, double a, double b,
int rtype, double smax = 0, double smin = 0)
{
double scale = 1, shift = 0;
if (smax - 0 < DBL_EPSILON)
{
cv::minMaxLoc(_src, &smin, &smax, 0, 0);
}
double dmin = MIN(a, b), dmax = MAX(a, b);
scale = (dmax - dmin) * (smax - smin > DBL_EPSILON ? 1. / (smax - smin) : 0);
shift = dmin - smin * scale;
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
cv::Mat res;
res.create(_src.size(), CV_MAKETYPE(rtype, cn));
cv::Mat src = _src, dst = res;
src.convertTo(dst, rtype, scale, shift);
_dst = res;
相应的头文件要包含。通过这样的转换,可以把GDAL读取的数据用Mat来表示。这样做的目的是项目小组已经将数据读取封装成为输出分块的Mat,将算法的输入输出改为Mat, 算法的调用更方便,算法本身不需要考虑数据分块等问题。