天天看点

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

前言:

秦梓航同志是我的老朋友加老搭档,他从初中开始接触机器人,初三开始自己出项目,高二时他和冶子奕用树莓派出的“小七”智能机器人进了高中机器人大赛的国赛,这些年获得许多机器人相关比赛的国内省内奖项,是七号库房的第一个社长。秦梓航同志到大学之后开始接触OpenCV,早期在学校的Robomaster战队搞自瞄算法,后期在一家公司搞机器人/无人机算法。论专业水平,秦梓航同志一直是老吴的领路人,这篇博客可以作为一个标杆,这是以后写文章需要有的一种专业态度。

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

这篇文章主要讲的通过对直方图进行数学累计分布函数处理从而使得图像参数直方图均衡化。通过获得像素值的概率密度求累计概率,然后乘位数最大量像素映射。文章同时包括了代码实现,后面我会将其整理后发出。

正文:全局直方图均衡化主要应用在图像增益之中,用于提升图像的对比度,简单来说就是让图像亮的地方变暗一点,暗的地方变亮一些,整体提升图像的动态范围.上面的话听起来可能不是那么直观,下面放两张图进行一下对比应该会好一些 P S :此处直方图就是对图像的灰阶/亮度信息进行统计记录每个亮度等级的数量.

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)
c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

这是原图像未经过处理,左边是目前的亮度直方图 ,可以看到亮度范围比较集中在中间区域.     

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)
c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

         这是经过全局直方图均衡化之后的图像,经过参与处理之后的图像对比度明显提高.同样的通过亮度直方图可以看出目前的图像动态范围较之前有所提升。下面进入具体的理论部分首先看一下公式

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

或者是这个

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

                                                        当然这两个公式其实差不多啦,表达的都是图像的累计分布函数,乍一看确实有点摸不着头脑,不知道这都是些啥?具体意义是什么?这些我们先放到一边,先来看几个问题. 1.什么是累计分布函数?

严格的数学定义和性质很容易查找到,我这里还是引用一下

累积分布函数(Cumulative Distribution Function),又叫分布函数,是概率密度函数的积分,能完整描述一个实随机变量X的概率分布。一般以大写CDF标记,,与概率密度函数probability density function(小写pdf)相对。

累积分布函数表示:对离散变量而言,所有小于等于a的值出现概率的和.

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)
c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

如果是在图像处理中,密度函数的含义也就是图像中的亮度/灰阶所占图像总像素的多少,而累计分布函数则是当前亮度等级对于图像整体出现的累计概率,根据CDF的定义这是很明显的一件事.

举个例子:有如下2*3单通道8位(0-255)图像,像素值如图所示

100 15
150 160
40 100

则密度函数的值为:

像素值 密度/出现的概率h(I) 累计概率( C(I) )
40 出现1次:1/6 1/6
100 出现2次:2/6 = 1/3 1/6 + 1/3= 3/6 
150 出现2次:2/6 = 1/3 1/6 + 1/3 +1/3 = 5/6
160 出现1次: 1/6 = 1/6 1/6 + 1/3 +1/3+1/6 = 1

2.为什么使用累计分布函数可以完成全局直方图均衡化?

直方图均衡化的目标是提升图像的动态范围从而进行图像增益,我们其实是通过累计分布函数建立一种映射关系,我们希望亮度值可以更加均匀的散布在整个亮度范围之内对于8位图像来说是(0-255),同样的图像的累计分布函数很好的反应了当前图像亮度的分布规律,我们只需要将其映射至[0,255]的整个空间之内便可以达到我们想要的效果.还是举个例子:如上面的表格所示

像素值 密度概率h(I) 累计概率 C(I) 最终值(取整)
40 出现1次:1/6 1/6 1/6*255 = 42.49=42
100 出现2次:2/6 = 1/3 1/6 + 1/3= 3/6 3/6*255  =128
150 出现2次:2/6 = 1/3 1/6 + 1/3 +1/3 = 5/6 5/6*255 = 213
160 出现1次: 1/6 = 1/6 1/6 + 1/3 +1/3+1/6 = 1 1*255 = 255

最终 图像

128 213
213 255
42 128

可以看到图像的整体动态范围由原先的 160 - 40 = 120 变为了 255 - 42 = 213 范围有所提升,从直方图结果来看整体分布也更加均匀 ,

再来看一下之前忽略的函数部分:

c++ opencv mat_(转)Opencv 和 C++实现全局直方图均衡化(原理到实践)

其中c(I)也就是累计概率函数,1/N*h(I)就是密度概率我们要做的就是如下几步:1.获取图像的灰度信息.2.获取像素值的密度概率3.根据密度概率求出累计概率4.将累计概率*255完成最终映射至此原理部分结束.程序部分

#include
#include
    using namespace cv;    using namespace std ;    Mat src,gray,dst;    void Test(Mat &img);    int main(int argc, char *argv[])
    {src =imread("/home/qinzihang/opencv-4.2.0/samples/data/lena.jpg");        resize(src, src, Size(200,200));        cvtColor(src, src,COLOR_BGR2GRAY);        Test(src);        imshow("dst", src);        waitKey(0);    }void Test(Mat &img)
    {float gray_l[256] = {0};        float HI[256] = {0};//图像亮度密度1/N*h(I)        float CDF[256] = {0};//累积分布函数        int rows=img.rows;        int cols=img.cols;        Mat draw_mat = Mat::zeros(600,600,CV_8U);        cout << rows << endl;//x        for(int i=0; i; i++)
        {for(int j=0; j; j++)
            {
                uchar t;                if(img.channels()==1)
                {
                    t=img.at(i,j);                    gray_l[t]++;//获得图像亮度信息                }
            }
        }for(int i = 0; i <256; i++)
        {
            line(draw_mat,Point(i+150,600-1),Point(i+150, 600-gray_l[i]),254);            imshow("draw", draw_mat);            HI[i] = (gray_l[i]/(img.cols*img.rows*1.0f));//获得密度概率            cout << HI[i] << endl;        }for(int i = 0; i < 255; i++)
        {if(i == 0)
            {
                CDF[0] = HI[ 0];            }else            {
                CDF[i] = (CDF[i-1] +HI[i]);//C(I) = C(I - 1 ) +h(I)                cout << CDF[i] << endl;            }
        }for(int i=0; i; i++)
        {for(int j=0; j; j++)
            {
                uchar t;                if(img.channels()==1)
                {
                    t=img.at(i,j);                    img.at(i,j) = 255*CDF[t];//完成图像重映射至0-255                }
            } draw_mat = Mat::zeros(600,600,CV_8U);        }
        imshow("yes",img);    }
           

本文仅仅是我的一些浅薄的看法和对这个算法的一些思考,整个过程不是非常的严谨,希望看了之后多少对初学者理解全局直方图均衡化这个算法有一些帮助,文章中的错误也希望大家可以指出。

继续阅读