OpenCV 局部自适应对比度增强ACE算法

图像的对比度增强算法在很多场合都有着重要的应用,特别是在医学图像上,这是因为在众多疾病的诊断中,医学图像的视觉检查时很有必要的。而医学图像由于本身及成像条件的限制,图像的对比度很低。因此,在这个方面已经开展了很多的研究。这种增强算法一般都遵循一定的视觉原则。众所周知,人眼对高频信号(边缘处等)比较敏感。虽然细节信息往往是高频信号,但是他们时常嵌入在大量的低频背景信号中,从而使得其视觉可见性降低。因此适当的提高高频部分能够提高视觉效果并有利于诊断。

     在这一方面,传统的线性对比度拉升以及直方图均衡化是使用的最为广泛的全局图像增强方法。对比度拉升线性的调整了图像的动态范围,而直方图均衡化栖利用累计直方图分布概率重新映射图像的数据。这些方法虽然简单,但是都没有考虑到局部的信息。并且,全局直方图均衡化(GHE)还会产生使得一些噪音过度加强。

     在局部对比度增强方面,有两种方式是最为有名的,一种是自适应直方图均衡化(AHE),这个算法可以参考我的博文https://blog.csdn.net/cyf15238622067/article/details/89202361

https://blog.csdn.net/cyf15238622067/article/details/87798128

还有一种就是自适应对比度增强(ACE)。AHE算法使用局部的直方图的相关信息对数据进行映射。这改变了图像的对比度,但是需要大量的计算。后来有人利用了双线性差值技术克服了这个问题,首先将图像分块,然后分别计算这些快内部的映射关系。为了增强某一个像素点的值,映射关系通过与这个像素所在块相邻的四个块的映射关系差值获得。在这个算法中,仅仅需要一个块大小的参数(在我的博文中还对参数进行了扩展)。

      ACE算法采用了反锐化掩模技术,我们对此过程解释如下:首先图像被分成两个部分。一是低频的反锐化掩模(unsharp mask)部分,可以通过图像的低通滤波(平滑,模糊技术)获得。二是高频成分,可以过原图减去反锐化掩模获取。然后高频部分被放大(放大系数即为对比度增益CG)并加入到反锐化掩模中去,最后得到增强的图像。ACE算法的核心就是如何计算CG,这里将介绍两种简单的CG计算方法。

自适应对比度增强

在图像处理的方法中,自适应方法是与图像本身信息相关,根据图像对图特征对图像进行处理的一系列方法,这些方法往往具有更好的鲁棒性、普适性。而本文中提到的这种ACE方法由NarendraPM Narendra P MNarendraPM等人在《Real-Time Adaptive Contrast Enhancement》中提到,原理简单易懂,有兴趣的朋友可以点击链接去阅读。https://ieeexplore.ieee.org/abstract/document/4767166

对于图像中的每一个点,分别计算其局部均值与局部标准差;

        上述式子中,f(s,k) 代表坐标为(s,k) 的点的像素值,M(i,j) 为以点(i,j)为中心,窗口大小为[(2n+1),(2m+1)] [(2n+1),(2m+1)][(2n+1),(2m+1)]的区域的局部均值,对应的σ2(i,j) \sigma ^{2}(i,j)σ2(i,j)为局部的方差,σ(i,j) \sigma (i,j)σ(i,j)为局部图像的标准差。
       在求得局部均值与标准差后,就可以对图像进行增强了,具体的公式如下;

                                                    

再来分析一下,上面式子的含义;如果将每个点的局部均值M(i,j) M(i,j)M(i,j)构成一张图,其实就是均值滤波的结果,而在《数字图像傅里叶变换的物理意义及简单应用》中,我提到过均值滤波是一种低通滤波,获得的是图像的低频部分,也就是背景部分,f(i,j)−M(i,j) f(i,j)-M(i,j)f(i,j)−M(i,j)就可以用来量化图像中的一个点是高频还是低频。而在一般情况下,G GG 都是大于1的,所以通过 G(f(i,j)−M(i,j)) G(f(i,j)-M(i,j))G(f(i,j)−M(i,j))可以实现对图像的高频部分的放大,进而对图像进行增强。
                                    

再来看看参数G GG,经过上面的过程可以看出,如果G GG 是一个固定参数,比如都取5,通过式子中的局部均值,我们已经能够将图像实现一定程度上的自适应增强了。那么为什么还要在参数G中引入标准差呢?

我们回忆一下对比度增强的初衷,对比度增强是为了让本身对比度不强的图像的对比度变得明显,而对本身对比度很强的图像,是没必要做增强的。那么在同一图像中,我们尤其需要增强对比度不强的部分。而方差表示的是图像的像素值的均匀性,我们可以认为方差越大的局部区域,其像素值越不均匀,对比度越强;反之,方差越小的局部区域,其像素值越均匀,对比度越弱。因此,在参数G GG中除以了局部标准差,可以让图像中对比度较弱的部分的增强效果更加明显。

       其次,如果对整张图像中所有点进行等比例增强,图像中本身就是高频的部分出现过增强的现象,图像看起来十分奇怪。
 

彩色图像的ACE

在网上看到有人说,对彩色图像增强可以分别对RGB三通道进行增强后进行合并。这个观点是错误的,因为分别对各个通道进行增强,会引起图像色相的变化,图像会变的不是其原来的颜色了。

所以需要将图像转到HSI色彩空间,或者是YCrCb颜色空间。前者只需要对I亮度通道进行增强,而H、S分别代表的色调和饱和度通道不需要变化。后者用Y通道表示亮度,只需要对Y通道进行增强即可。增强之后再合并通道,转换回RGB空间便完成了对彩色图像的增强。
 

代码实现:

#include<opencv2/opencv.hpp>
#include<vector>
#include<iostream>

using namespace std;
using namespace cv;


//--------------------
//函数功能:获取图像的局部均值与局部标准差的图
//函数名称:getVarianceMean
//函数参数:Mat &scr:输入图像,为单通道;
//函数参数:Mat &meansDst:计算得到的均值的图,均值的值与输入图像中的点一一对应,为单通道;
//函数参数:Mat &varianceDst:计算得到的标准差的图,标准差的值与输入图像中的点一一对应,为单通道;
//函数参数:int winSize:局部均值的窗口大小,应为单数;
//返回类型:bool
//--------------------

bool getVarianceMean(Mat &scr, Mat &meansDst, Mat &varianceDst, int winSize)
{
    if (!scr.data)  //判断图像是否被正确读取;
    {
        cerr << "获取方差与均值的函数读入图片有误";
        return false;
    }

    if (winSize % 2 == 0)
    {
        cerr << "计算局部均值与标准差的窗口大小应该为单数";
        return false;
    }

    Mat copyBorder_yChannels;                        //扩充图像边界;
    int copyBorderSize = (winSize - 1) / 2;
    copyMakeBorder(scr, copyBorder_yChannels, copyBorderSize, copyBorderSize, copyBorderSize, copyBorderSize, BORDER_REFLECT);

    for (int i = (winSize - 1) / 2; i < copyBorder_yChannels.rows - (winSize - 1) / 2; i++)
    {
        for (int j = (winSize - 1) / 2; j < copyBorder_yChannels.cols - (winSize - 1) / 2; j++)
        {
            
            Mat temp = copyBorder_yChannels(Rect(j - (winSize - 1) / 2, i - (winSize - 1) / 2, winSize, winSize));   //截取扩展后的图像中的一个方块;

            Scalar  mean;
            Scalar  dev;
            meanStdDev(temp, mean, dev);
            
            varianceDst.at<float>(i - (winSize - 1) / 2, j - (winSize - 1) / 2) = dev.val[0];     ///一一对应赋值;
            meansDst.at<float>(i - (winSize - 1) / 2, j - (winSize - 1) / 2) = mean.val[0];
        }
    }

    
    return true;
}

//--------------------------
//函数功能:获取图像的局部均值与局部标准差的图
//函数名称:adaptContrastEnhancement
//函数参数:Mat &scr:输入图像,为三通道RGB图像;
//函数参数:Mat &dst:增强后的输出图像,为三通道RGB图像;
//函数参数:int winSize:局部均值的窗口大小,应为单数;
//函数参数:int maxCg:增强幅度的上限;
//返回类型:bool
//--------------------

bool adaptContrastEnhancement(Mat &scr, Mat &dst, int winSize,int maxCg)
{
    if (!scr.data)  //判断图像是否被正确读取;
    {
        cerr << "自适应对比度增强函数读入图片有误";
        return false;
    }

    Mat ycc;                        //转换空间到YCrCb;
    cvtColor(scr, ycc, COLOR_RGB2YCrCb);

    vector<Mat> channels(3);        //分离通道;
    split(ycc, channels);

    
    Mat localMeansMatrix(scr.rows , scr.cols , CV_32FC1);
    Mat localVarianceMatrix(scr.rows , scr.cols , CV_32FC1);
    
    if (!getVarianceMean(channels[0], localMeansMatrix, localVarianceMatrix, winSize))   //对Y通道进行增强;
    {
        cerr << "计算图像均值与标准差过程中发生错误";
        return false;
    }

    Mat temp = channels[0].clone();

    Scalar  mean;
    Scalar  dev;
    meanStdDev(temp, mean, dev);

    float meansGlobal = mean.val[0];
    Mat enhanceMatrix(scr.rows, scr.cols, CV_8UC1);

    for (int i = 0; i < scr.rows; i++)            //遍历,对每个点进行自适应调节
    {
        for (int j = 0; j < scr.cols; j++)
        {
            if (localVarianceMatrix.at<float>(i, j) >= 0.01)
            {
                float cg = 0.2*meansGlobal / localVarianceMatrix.at<float>(i, j);
                float cgs = cg > maxCg ? maxCg : cg;
                cgs = cgs < 1 ? 1 : cgs;
                
                int e = localMeansMatrix.at<float>(i, j) + cgs* (temp.at<uchar>(i, j) - localMeansMatrix.at<float>(i, j));
                if (e > 255){ e = 255; }
                else if (e < 0){ e = 0; }
                enhanceMatrix.at<uchar>(i, j) = e;
            }
            else
            {
                enhanceMatrix.at<uchar>(i, j) = temp.at<uchar>(i, j);
            }
        }
            
    }
    
    channels[0] = enhanceMatrix;    //合并通道,转换颜色空间回到RGB
    merge(channels, ycc);

    cvtColor(ycc, dst, COLOR_YCrCb2RGB);
        
}

//----------------
//主函数
//----------------
void main()
{
    Mat scr = imread("temp1.jpg", IMREAD_COLOR);  //以彩色的方式读入图像
         
    if (!scr.data)               //判断图像是否被正确读取;
    {
        return;
    }

    const int WINSIZE = 15;      //WINSIZE表示求均值与方差的窗口大小,应该是单数
    const int MAXCG = 10;        //设定最大的增强比例

    Mat dst = scr.clone();

    adaptContrastEnhancement(scr, dst, WINSIZE,MAXCG);
    
    imwrite("01.png", dst);      //写出图像
    
    return;
}

效果图:


---------------------
作者:Dwyane05
来源:CSDN
原文:https://blog.csdn.net/cyf15238622067/article/details/89202725
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是基于杜鹃搜索算法的CLAHE自适应阈值对比度增强算法的示例代码: ```python import cv2 import numpy as np import math # 定义CLAHE算法 def clahe(image, clip_limit=2.0, grid_size=(8, 8)): clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size) return clahe.apply(image) # 定义杜鹃搜索算法 def cuckoo_search(fit_func, dim, lb, ub, pa=0.25, gen=100): pop_size = 50 nest = np.random.rand(pop_size, dim) * (ub - lb) + lb fitness = np.zeros(pop_size) best_nest_index = np.zeros(gen) best_nest = np.zeros((gen, dim)) fmin = fit_func(nest[0, :]) best_nest[0, :] = nest[0, :] for i in range(pop_size): fitness[i] = fit_func(nest[i, :]) if fitness[i] < fmin: fmin = fitness[i] best_nest[0, :] = nest[i, :] for it in range(1, gen): new_nest = np.zeros((pop_size, dim)) for i in range(pop_size): u = np.random.rand(dim) < pa step_size = np.random.rand() * (nest[np.random.randint(pop_size), :] - nest[np.random.randint(pop_size), :]) new_nest[i, :] = nest[i, :] + step_size * u for i in range(pop_size): for j in range(dim): if new_nest[i, j] < lb: new_nest[i, j] = lb if new_nest[i, j] > ub: new_nest[i, j] = ub for i in range(pop_size): fitness_new = fit_func(new_nest[i, :]) if fitness_new < fitness[i]: fitness[i] = fitness_new nest[i, :] = new_nest[i, :] if fitness_new < fmin: fmin = fitness_new best_nest[it, :] = new_nest[i, :] best_nest_index[it] = fmin return best_nest, best_nest_index # 定义自适应阈值CLAHE算法 def adaptive_clahe(image, clip_limit=2.0, grid_size=(8, 8), search_gen=50, search_pa=0.25): # 定义适应度函数 def fitness_func(threshold): img = np.array(image) img[img < threshold] = 0 img[img >= threshold] = 255 img = clahe(img, clip_limit=clip_limit, grid_size=grid_size) mse = np.mean((img - image) ** 2) return mse # 搜索最佳阈值 lb = 0 ub = 255 dim = 1 best_nest, best_nest_index = cuckoo_search(fitness_func, dim, lb, ub, pa=search_pa, gen=search_gen) threshold = best_nest[-1, 0] # 对图像进行CLAHE处理 img = np.array(image) img[img < threshold] = 0 img[img >= threshold] = 255 img = clahe(img, clip_limit=clip_limit, grid_size=grid_size) return img # 测试代码 if __name__ == '__main__': img = cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE) img_adaptive_clahe = adaptive_clahe(img, clip_limit=2.0, grid_size=(8, 8), search_gen=50, search_pa=0.25) cv2.imshow('Original Image', img) cv2.imshow('Adaptive CLAHE Image', img_adaptive_clahe) cv2.waitKey(0) cv2.destroyAllWindows() ``` 该代码使用了numpy和OpenCV库,首先定义了CLAHE算法和杜鹃搜索算法的函数,然后定义了自适应阈值CLAHE算法的函数,最后进行测试并显示结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值