直方图均衡化算法原理与c++实现

写在前面
直方图均衡是图像增强的基础算法,本质上是一种非线性拉伸,使图像的灰度级分布变得较为均匀,从而增强图像的对比度,使这些不够明显的图像细节清晰可见。掌握一种算法,必须要弄清楚它的优势和缺点,要知道它的适用场合,这样才能灵活的运用这些算法。

直方图均衡算法的优势:对于背景和前景都太亮或者太暗的图像非常有用,计算量不大,完全可以满足实时。并且是可逆操作,也就是说如果已知均衡化函数,那么就可以恢复原始的直方图。放张图片吧:

                                                                  

 直方图均衡算法的缺点:它也有缺陷,它对数据的处理是不加选择的,也就是说,它可能会增加背景噪声的对比度并且降低有用信号的对比度。所以对于特征提取要求比较高的场合慎用,看下图:

                                        

 
算法原理
直方图均衡的目标是要让图像的灰度级分布均匀,但是同时需要保证输出的灰度级不少于相应的输入灰度级(少了就丢失信息了),且保证输出的灰度范围与输入灰度范围相同,因此对于变换函数f(x)需要满足如下条件:

  1. f(x)在0<=x<=L−1上单调递增,其中L表示灰度级数量(例如8bit图L=256)
  2. f(x)的范围是[0,L−1]

那么根据冈萨雷斯的书中介绍,在图像处理中,有一个重要的函数,能够满足上面的条件: 

                                           

那么为什么经过它变换后的直方图是均匀的呢,这就涉及了概率论的知识,具体的推导可以看这篇:

https://blog.csdn.net/qianqing13579/article/details/52422364

我们注意到,图像是离散的,可以使用求和代替积分,差分代替微分,所以上述的变换函数就是:

                                          

其中h(xi)表示直方图中每个灰度级像素的个数, ww和 hh分别表示图像的宽和高。

上式就被称为直方图均衡化。

代码实现

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
 
void Histogram_equalization(cv::Mat& src, cv::Mat& dst){
    CV_Assert(src.depth() == CV_8U);
    src.copyTo(dst);
    int nr = src.rows;
    int nc = src.cols;
    int pixnum = nr*nc;
    if (src.channels() == 1){
        //统计直方图
        int gray[256] = { 0 };
        for (int i = 1; i < nr; ++i){
            const uchar* ptr = src.ptr<uchar>(i);
            for (int j = 0; j < nc; ++j){
                gray[ptr[j]]++;
            }
        }
        //计算分布函数
        int LUT[256];
        int sum = 0;
        for (int k = 0; k < 256; k++){
            sum = sum + gray[k];
            LUT[k] = 255 * sum / pixnum;
        }
        //灰度变换(赋值)
        for (int i = 1; i < nr; ++i){
            const uchar* ptr_src = src.ptr<uchar>(i);
            uchar* ptr_dst = dst.ptr<uchar>(i);
            for (int j = 0; j < nc; ++j){
                ptr_dst[j] = LUT[ptr_src[j]];
            }
        }
    }
    else{
        //统计直方图
        int B[256] = { 0 };
        int G[256] = { 0 };
        int R[256] = { 0 };
        for (int i = 0; i < nr; ++i){
            for (int j = 0; j < nc; ++j){
                B[src.at<cv::Vec3b>(i, j)[0]]++;
                G[src.at<cv::Vec3b>(i, j)[1]]++;
                R[src.at<cv::Vec3b>(i, j)[2]]++;
            }
        }
        //计算分布函数
        int LUT_B[256], LUT_G[256], LUT_R[256];
        int sum_B = 0, sum_G = 0, sum_R = 0;
        for (int k = 0; k < 256; k++){
            sum_B = sum_B + B[k];
            sum_G = sum_G + G[k];
            sum_R = sum_R + R[k];
            LUT_B[k] = 255 * sum_B / pixnum;
            LUT_G[k] = 255 * sum_G / pixnum;
            LUT_R[k] = 255 * sum_R / pixnum;
        }
        //灰度变换(赋值)
        for (int i = 0; i < nr; ++i){
            for (int j = 0; j < nc; ++j){
                dst.at<cv::Vec3b>(i, j)[0] = LUT_B[src.at<cv::Vec3b>(i, j)[0]];
                dst.at<cv::Vec3b>(i, j)[1] = LUT_G[src.at<cv::Vec3b>(i, j)[1]];
                dst.at<cv::Vec3b>(i, j)[2] = LUT_R[src.at<cv::Vec3b>(i, j)[2]];
            }
        }
    }
}
 
    int main(){
        cv::Mat src = cv::imread("..../art.jpg");
        if (src.empty()){
            return -1;
        }
        cvtColor(src, src, CV_RGB2GRAY);
        cv::Mat dst;
 
        Histogram_equalization(src, dst);//直方图均衡
 
        cv::namedWindow("src");
        cv::imshow("src", src);
        cv::namedWindow("dst");
        cv::imshow("dst", dst);
        cv::waitKey(0);
    }

效果: 

原图


 
输出
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_40647819/article/details/88383606

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值