去雾原理原理是根据何凯明博士的《Single Image Haze Removal Using Dark Channel Prior》这篇文章,介绍见https://www.cnblogs.com/Imageshop/p/3281703.html。及Python代码见:OpenCV—Python 暗通道图像去雾算法_opencv 暗通道 函数-CSDN博客。
本文主要介绍C++实现,工程文件见:https://download.csdn.net/download/qq_34902877/12017480。
1、求暗通道
窗口的大小size,这个对结果来说是个关键的参数,窗口越大,其包含暗通道的概率越大,暗通道也就越黑,去雾的效果越不明显,一般窗口大小在11-51之间,即半径在5-25之间。
Mat DarkChannel(Mat srcImg, int size)
{
vector<Mat> chanels;
split(srcImg, chanels);
//求RGB三通道中的最小像像素值
Mat minChannel = (cv::min)((cv::min)(chanels[0], chanels[1]), chanels[2]);
Mat kernel = getStructuringElement(MORPH_RECT, Size(size, size));
Mat dark(minChannel.rows, minChannel.cols, CV_32FC1);
erode(minChannel, dark, kernel); //图像腐蚀
return dark;
}
2、求全球大气光值A
void AtmLight(Mat src, Mat dark, float outA[3])
{
int row = src.rows;
int col = src.cols;
int imgSize = row*col;
//将暗图像和原图转为列向量
vector<float> darkVector = dark.reshape(1, imgSize);
Mat srcVector = src.reshape(3, imgSize);
//按照亮度的大小取前0.1%的像素(亮度高)
int numpx = int(max(floor(imgSize / 1000), 1.0));
vector<int> indices = argsort(darkVector);
vector<int> dstIndices(indices.begin() + (imgSize - numpx), indices.end());
for (int i = 0; i < numpx; ++i)
{
outA[0] += srcVector.at<Vec3f>(dstIndices[i], 0)[0];
outA[1] += srcVector.at<Vec3f>(dstIndices[i], 0)[1];
outA[2] += srcVector.at<Vec3f>(dstIndices[i], 0)[2];
}
outA[0] /= numpx;
outA[1] /= numpx;
outA[2] /= numpx;
}
3、计算计算透射率预估值
中的omega具有着明显的意义,其值越小,去雾效果越不明显
Mat TransmissionEstimate(Mat src, float outA[3], int size, float omega)
{
Mat imgA = Mat::zeros(src.rows, src.cols, CV_32FC3);
vector<Mat> chanels;
split(src, chanels);
for (int i = 0; i < 3; ++i)
{
chanels[i] = chanels[i] / outA[i];
}
merge(chanels, imgA);
Mat transmission = 1 - omega*DarkChannel(imgA, size); //计算透射率预估值
return transmission;
}
4、导向滤波
Mat Guidedfilter(Mat src, Mat te, int r, float eps)
{
Mat meanI, meanT, meanIT, meanII, meanA, meanB;
boxFilter(src, meanI, CV_32F, Size(r, r));
boxFilter(te, meanT, CV_32F, Size(r, r));
boxFilter(src.mul(te), meanIT, CV_32F, Size(r, r));
Mat covIT = meanIT - meanI.mul(meanT);
boxFilter(src.mul(src), meanII, CV_32F, Size(r, r));
Mat varI = meanII - meanI.mul(meanI);
Mat a = covIT / (varI + eps);
Mat b = meanT - a.mul(meanI);
boxFilter(a, meanA, CV_32F, Size(r, r));
boxFilter(b, meanB, CV_32F, Size(r, r));
Mat t = meanA.mul(src) + meanB;
return t;
}
5、通过导向滤波计算透射率
Mat TransmissionRefine(Mat src, Mat te)
{
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
gray.convertTo(gray, CV_32F);
gray /= 255;
int r = 60;
float eps = 0.0001;
Mat t = Guidedfilter(gray, te, r, eps);
return t;
}
6、图像去雾
Mat Defogging(Mat src, Mat t, float outA[3], float tx)
{
Mat dst = Mat::zeros(src.rows, src.cols, CV_32FC3);
t = (cv::max)(t, tx); //设置阈值当投射图t 的值很小时,会导致图像整体向白场过度
vector<Mat> chanels;
split(src, chanels);
for (int i = 0; i < 3; ++i)
{
chanels[i] = (chanels[i] - outA[i]) / t + outA[i];
}
merge(chanels, dst);
dst *= 255; //归一化还原
return dst;
}
结果展示: