OpenCV图像增强(二)——Retinex图像增强

it2022-05-05  185

前言

1.Retinex图像增强是一种高动态范围图像的新色调映射技术。而基础理论是,物体的颜色是由物体对长波(红色)、中波(绿色)、短波(蓝色)光线的反射能力来决定的,而不是由反射光强度的绝对值来决定的,物体的色彩不受光照非均匀性的影响,具有一致性,即retinex是以色感一致性(颜色恒常性)为基础的。不同于传统的线性、非线性的只能增强图像某一类特征的方法,Retinex可以在动态范围压缩、边缘增强和颜色恒常三个方面达到平衡,因此可以对各种不同类型的图像进行自适应的增强。 2.Retinex图像增强包括两个步骤,全局适应和人类视觉系统的局部自适应。在局部自适应过程中,这里使用引导滤波器代替原本的高斯滤波器以减少晕圈伪影。为了保证良好的再现和动态范围压缩,使用基于场景的亮度值的对比度增强因子。此外,引入自适应非线性偏移来处理对数函数的非线性强度。 3.导向滤波作为一种保边滤波,可以运用在很多场合,比如美颜,去雾。

代码实现

void ALTMRetinex(const Mat& src, Mat &dst, bool LocalAdaptation = false, bool ContrastCorrect = true) { Mat temp, src_gray; src.convertTo(temp, CV_32FC3); //灰度图 cvtColor(temp, src_gray, CV_BGR2GRAY); double LwMax; //得到最大值 minMaxLoc(src_gray, NULL, &LwMax); Mat Lw_; const int num = src.rows * src.cols; //计算每个数组元素绝对值的自然对数 cv::log(src_gray + 1e-3f, Lw_); //矩阵自然指数 float LwAver = exp(cv::sum(Lw_)[0] / num); Mat Lg; log(src_gray / LwAver + 1.f, Lg); //矩阵除法 cv::divide(Lg, log(LwMax / LwAver + 1.f), Lg); //局部自适应 Mat Lout; if (LocalAdaptation) { int kernelSize = floor(std::max(3, std::max(src.rows / 100, src.cols / 100))); Mat Lp, kernel = cv::getStructuringElement(MORPH_RECT, Size(kernelSize, kernelSize)); cv::dilate(Lg, Lp, kernel); Mat Hg = guidedFilter(Lg, Lp, 10, 0.01f); double eta = 36; double LgMax; cv::minMaxLoc(Lg, NULL, &LgMax); Mat alpha = 1.0f + Lg * (eta / LgMax); Mat Lg_; cv::log(Lg + 1e-3f, Lg_); float LgAver = exp(cv::sum(Lg_)[0] / num); float lambda = 10; float beta = lambda * LgAver; cv::log(Lg / Hg + beta, Lout); cv::multiply(alpha, Lout, Lout); cv::normalize(Lout, Lout, 0, 255, NORM_MINMAX); } else { cv::normalize(Lg, Lout, 0, 255, NORM_MINMAX); } Mat gain(src.rows , src.cols, CV_32F); for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { float x = src_gray.at<float>(i, j); float y = Lout.at<float>(i, j); if (0 == x) gain.at<float>(i, j) = y; else gain.at<float>(i, j) = y / x; } } Mat bgr[3]; cv::split(temp, bgr); if (ContrastCorrect) { // 校正图像对比度 bgr[0] = (gain.mul(bgr[0] + src_gray) + bgr[0] - src_gray) *0.5f; bgr[1] = (gain.mul(bgr[1] + src_gray) + bgr[1] - src_gray) *0.5f; bgr[2] = (gain.mul(bgr[2] + src_gray) + bgr[2] - src_gray) *0.5f; } else { cv::multiply(bgr[0], gain, bgr[0]); cv::multiply(bgr[1], gain, bgr[1]); cv::multiply(bgr[2], gain, bgr[2]); } cv::merge(bgr, 3, dst); dst.convertTo(dst, CV_8UC3); } //导向滤波器 Mat guidedFilter(cv::Mat& I, cv::Mat& p, int r, float eps) { /* × GUIDEDFILTER O(N) time implementation of guided filter. × × - guidance image: I (should be a gray-scale/single channel image) × - filtering input image: p (should be a gray-scale/single channel image) × - local window radius: r × - regularization parameter: eps */ cv::Mat _I; I.convertTo(_I, CV_32FC1); I = _I; cv::Mat _p; p.convertTo(_p, CV_32FC1); p = _p; //因为opencv自带的boxFilter()中的Size,比如9x9,我们说半径为4 r = 2 * r + 1; //mean_I = boxfilter(I, r) ./ N; cv::Mat mean_I; cv::boxFilter(I, mean_I, CV_32FC1, cv::Size(r, r)); //mean_p = boxfilter(p, r) ./ N; cv::Mat mean_p; cv::boxFilter(p, mean_p, CV_32FC1, cv::Size(r, r)); //mean_Ip = boxfilter(I.*p, r) ./ N; cv::Mat mean_Ip; cv::boxFilter(I.mul(p), mean_Ip, CV_32FC1, cv::Size(r, r)); //cov_Ip = mean_Ip - mean_I .* mean_p; % this is the covariance of (I, p) in each local patch. cv::Mat cov_Ip = mean_Ip - mean_I.mul(mean_p); //mean_II = boxfilter(I.*I, r) ./ N; cv::Mat mean_II; cv::boxFilter(I.mul(I), mean_II, CV_32FC1, cv::Size(r, r)); //var_I = mean_II - mean_I .* mean_I; cv::Mat var_I = mean_II - mean_I.mul(mean_I); //a = cov_Ip ./ (var_I + eps); % Eqn. (5) in the paper; cv::Mat a = cov_Ip / (var_I + eps); //b = mean_p - a .* mean_I; % Eqn. (6) in the paper; cv::Mat b = mean_p - a.mul(mean_I); //mean_a = boxfilter(a, r) ./ N; cv::Mat mean_a; cv::boxFilter(a, mean_a, CV_32FC1, cv::Size(r, r)); //mean_b = boxfilter(b, r) ./ N; cv::Mat mean_b; cv::boxFilter(b, mean_b, CV_32FC1, cv::Size(r, r)); //q = mean_a .* I + mean_b; % Eqn. (8) in the paper; cv::Mat q = mean_a.mul(I) + mean_b; return q; }

运行结果

结语

我这里使用的库OpenCV版本是3.30,关于opencv学习,有兴趣的看我之前发的博客,可以加之前博客后面给的兴趣群。


最新回复(0)