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

前言

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学习,有兴趣的看我之前发的博客,可以加之前博客后面给的兴趣群。

  • 4
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
根据提供的引用内容,有两种常见的基于OpenCV图像增强算法可以使用C++实现: 1. 基于Retinex理论的图像增强算法:Retinex理论是一种常见的图像增强方法,它通过对图像的亮度和对比度进行调整来提高图像的质量。以下是一个使用OpenCV和C++实现的基于Retinex图像增强算法的示例代码: ```cpp #include <opencv2/opencv.hpp> cv::Mat enhanceImage(cv::Mat image) { cv::Mat enhancedImage; // 使用Retinex算法增强图像 cv::xphoto::createSimpleRetinex()->apply(image, enhancedImage); return enhancedImage; } int main() { // 读取图像 cv::Mat image = cv::imread("image.jpg"); // 图像增强 cv::Mat enhancedImage = enhanceImage(image); // 显示增强后的图像 cv::imshow("Enhanced Image", enhancedImage); cv::waitKey(0); return 0; } ``` 2. Zhang-Suen细化算法:Zhang-Suen细化算法是一种用于提取图像骨架的图像处理算法。以下是一个使用OpenCV和C++实现的Zhang-Suen细化算法的示例代码: ```cpp #include <opencv2/opencv.hpp> cv::Mat thinningImage(cv::Mat image) { cv::Mat binaryImage; // 将图像转换为图像 cv::cvtColor(image, binaryImage, cv::COLOR_BGR2GRAY); cv::threshold(binaryImage, binaryImage, 128, 255, cv::THRESH_BINARY); // 使用Zhang-Suen细化算法提取图像骨架 cv::ximgproc::thinning(binaryImage, binaryImage, cv::ximgproc::THINNING_ZHANGSUEN); return binaryImage; } int main() { // 读取图像 cv::Mat image = cv::imread("image.jpg"); // 图像细化 cv::Mat thinnedImage = thinningImage(image); // 显示细化后的图像 cv::imshow("Thinned Image", thinnedImage); cv::waitKey(0); return 0; } ``` 请注意,以上示例代码仅为演示目的,实际使用时可能需要根据具体需求进行适当的调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值