Qt、GDAL遥感影像显示
影像拉伸优化
使用GDAL读取影像数据之后,需要进行拉伸显示。比如读取landsat数据时,由于数据类型时ushort,如果直接显示,那么显示的结果就是一片空白。因此我们需要对数据进行拉伸,将ushort拉伸到0-255。因此出现以下三种拉伸方法。
方案1(最慢)
for(int index = 0; index < pImpObject->bandMap.length(); ++index) {
int bandMap = pImpObject->bandMap[index] + 1;
cv::Mat tmp = cv::Mat(buf.y, buf.x, GisEntsHelper::GDALType2GCType(dType, 1));
GDALRasterBand *pBand = pDataset->GetRasterBand(bandMap);
CPLErr err = pBand->RasterIO(GF_Read, read.x, read.y, size.x, size.y, tmp.data, tmp.cols, tmp.rows, dType, 0, 0);
GisEntsHelper::GisLineStretchImage(tmp, pImpObject->histArray[index], sType, GisEntsHelper::GDALTypeMaxValue(dType));
mats.push_back(tmp.clone());
}
void GisEntsHelper::GisLineStretchImage(cv::Mat &mat, const HistogramData &histData, OGis::StretchType sType, uint maskValue)
{
cv::Mat mask1;
cv::compare(mat, histData._noDataValue, mask1, cv::CMP_EQ);
mat.setTo(maskValue, mask1);
if(sType != OGis::kStretch_No) {
switch(sType) {
case OGis::kStretch_Line:
case OGis::kStretch_Line1:
case OGis::kStretch_Line2:
case OGis::kStretch_Line5:
cv::threshold(mat, mat, histData.minmax[sType - 1].x, 0, cv::THRESH_TOZERO);
cv::threshold(mat, mat, histData.minmax[sType - 1].y, 255, cv::THRESH_TRUNC);
mat = (mat - histData.minmax[sType - 1].x) * histData.minmax[sType - 1].z;
mat.convertTo(mat, CV_8U);
break;
case OGis::kStretch_Equalize:
cv::normalize(mat, mat, 255, 0, CV_MINMAX, CV_8U);
cv::equalizeHist(mat, mat); break;
}
}
}
方案2(中等)
for(int index = 0; index < pImpObject->bandMap.length(); ++index) {
int bandMap = pImpObject->bandMap[index] + 1;
cv::Mat tmp = cv::Mat(buf.y, buf.x, GisEntsHelper::GDALType2GCType(dType, 1));
GDALRasterBand *pBand = pDataset->GetRasterBand(bandMap);
CPLErr err = pBand->RasterIO(GF_Read, read.x, read.y, size.x, size.y, tmp.data, tmp.cols, tmp.rows, dType, 0, 0);
mats.push_back(tmp.clone());
}
GisEntsHelper::GisLineStretchImage(mats, pImpObject->histArray, sType, 0);
void GisEntsHelper::GisLineStretchImage(std::vector<cv::Mat> &mats, const HistogramDataArray &hists, OGis::StretchType sType, uint trans)
{
if(sType == OGis::kStretch_No) {
return;
}
std::vector<cv::Mat> temps = mats;
cv::Mat mask; mats.clear();
if(temps.size() == 3) {
mask = ((temps[0] == temps[0]) & (temps[1] == temps[0]) & (temps[2] == temps[0]));
temps[0].setTo(hists[0]._max, mask); temps[1].setTo(hists[1]._max, mask); temps[2].setTo(hists[2]._max, mask);
}
else {
mask = ~cv::Mat(temps[0] == temps[0]);
temps[0].setTo(hists[0]._max, mask);
}
for(int idx = 0; idx < temps.size(); ++idx) {
cv::Mat mat = temps[idx]; HistogramData hist = hists[idx];
cv::Mat result = cv::Mat(temps[0].rows, temps[0].cols, CV_8U);
if(sType <= OGis::kStretch_Line5) {
Double3 minmax = hist.minmax[sType - 1];
for(int row = 0; row < mat.rows; ++row) {
for(int col = 0; col < mat.cols; ++col) {
double val = GetMatValue(mat, row, col);
if(isnan(val)) {
result.at<byte>(row, col) = 255;
continue;
}
if(val < minmax.x) {
val = 0;
}
else if(val > minmax.y) {
val = 255;
}
else {
val = (val - minmax.x) * minmax.z;
}
result.at<byte>(row, col) = val;
}
}
}
else if(OGis::kStretch_SquareRoot == sType) {
if(hist._min < 0) {
mat -= hist._min;
}
mat.convertTo(mat, CV_32F);
mat = (mat - hist._min) * hist.minmax[sType - 1].x + hist.minmax[sType - 1].y;
mat.convertTo(result, CV_8U);
}
else if(OGis::kStretch_Logaithmic == sType) {
if(hist._min < 1) {
mat -= (hist._min - 1);
}
mat.convertTo(mat, CV_32F);
mat = (mat - hist._min) * hist.minmax[sType - 1].x + hist.minmax[sType - 1].y;
mat.convertTo(result, CV_8U);
}
else if(OGis::kStretch_Gaussian == sType) {
}
else if(OGis::kStretch_Equalize == sType) {
cv::normalize(mat, result, 255, 0, CV_MINMAX, CV_8U);
cv::equalizeHist(result, result);
}
mats.push_back(result.clone());
}
}
方案2的代码量虽然比较多,但是速度比方案1确实快了一点点。
方案3(最快)
for(int index = 0; index < pImpObject->bandMap.length(); ++index) {
int bandMap = pImpObject->bandMap[index] + 1;
cv::Mat tmp = cv::Mat(buf.y, buf.x, GisEntsHelper::GDALType2GCType(dType, 1));
GDALRasterBand *pBand = pDataset->GetRasterBand(bandMap);
CPLErr err = pBand->RasterIO(GF_Read, read.x, read.y, size.x, size.y, tmp.data, tmp.cols, tmp.rows, dType, 0, 0);
mats.push_back(tmp.clone());
}
GisEntsHelper::GisLineStretchImageEx(mats, pImpObject->histArray, sType, 0);
void GisEntsHelper::GisLineStretchImageEx(std::vector<cv::Mat> &mats, const HistogramDataArray &hists, OGis::StretchType sType, uint trans)
{
if(sType == OGis::kStretch_No) {
return;
}
std::vector<cv::Mat> temps;
cv::Mat mask;
if(mats.size() == 3) {
mask = ((mats[0] == hists[0]._noDataValue) & (mats[1] == hists[1]._noDataValue) & (mats[2] == hists[2]._noDataValue));
}
else {
mask = ~cv::Mat(mats[0] == mats[0]);
}
for(int idxMat = 0; idxMat < mats.size(); ++idxMat) {
mats[idxMat].setTo(hists[idxMat]._max, mask);
HistogramData hist = hists[idxMat]; cv::Mat result;
mats[idxMat].convertTo(result, CV_8U, 255.0 / (hist._max - hist._min), -255.0 * hist._min / (hist._max - hist._min));
if(OGis::kStretch_Equalize == sType) {
cv::equalizeHist(result, mats[idxMat]);
}
temps.push_back(result);
}
if(OGis::kStretch_Equalize == sType) {
return;
}
mats.clear();
for(int idxMat = 0; idxMat < temps.size(); ++idxMat) {
HistogramData hist = hists[idxMat];
cv::Mat lookTableMat = cv::Mat(1, 256, CV_8U);
uchar *pData = lookTableMat.data;
double valMin, valMax, factor;
Double3 minmax = hist.minmax[sType - 1];
double dMin = hist._min, dMax = hist._max;
if(sType <= OGis::kStretch_Line5) {
valMin = 255.0 * (minmax.x - hist._min) / (hist._max - hist._min);
valMax = 255.0 * (minmax.y - hist._min) / (hist._max - hist._min);
}
else if(sType == OGis::kStretch_SquareRoot) {
valMin = sqrt(0);
valMax = sqrt(255);
}
else if(sType == OGis::kStretch_Logaithmic) {
valMin = log(1);
valMax = log(256);
}
factor = 255.0 / (valMax - valMin);
for(int idx = 0; idx < 256; ++idx) {
double val = idx;
if(OGis::kStretch_SquareRoot == sType) val = sqrt(idx);
else if(OGis::kStretch_Logaithmic == sType) val = log(idx + 1);
if(val < valMin) pData[idx] = 0;
else if(val > valMax) pData[idx] = 255;
else pData[idx] = (val - valMin) * factor;
}
cv::Mat result = cv::Mat(temps[idxMat].rows, temps[idxMat].cols, CV_8U);
cv::LUT(temps[idxMat], lookTableMat, result);
mats.push_back(result.clone());
}
}
方案3使用OpenCV的查找表进行数据的拉伸。
分析原因
方案 | 原因 |
---|---|
方案1 | 对mat进行了6次遍历 |
方案2 | 对mat进行了2次遍历 |
方案3 | 对mat进行了2次遍历,第二次使用的是cv::LUT,这个效率不是遍历像素可比的 |