讲解直方图均衡化之前,先解释一下图像的统计直方图与累加概率。

1. 统计直方图,就是统计图像中每一个像素值的个数。比如对于8位的图像,每一个像素点的像素值取值范围是0~255,那么其统计直方图就是统计0~255中所有像素值在图像中的个数,比如0像素值有几个点、1像素值有几个点、2像素值有几个点......像素255有几个点,如下图所示:

2. 像素的概率,也就是该像素值的统计直方图值(像素数)除以图像的总像素数,假设像素x的统计直方图值为hist(x),图像的总像素数为size,那么该像素的概率为:

像素值x的累加概率,就是所有小于等于x的像素值的像素数,除以图像的总像素数,可按下式计算:

3. 接下来讲解直方图均衡化的原理。通常认为,图像的统计直方图分布越均匀,图像的质量越好,直方图均衡化可以提升图像的对比度和质量。比如有的图像整体较暗,那么说明其直方图中低像素值分布较多,高像素值分布较少,这时可以使用直方图均衡化来平衡其直方图分布,即减少低像素值,增加高像素值。从直观上看,均衡化的效果就是图像的部分区域相对原来变亮了,所以提升了图像的对比度。

直方图均衡化就是一个所有像素值重映射的过程。比如像素值x的直方图均衡化之后的值可按下式计算,其中n为图像的每一个像素点的位宽,对于灰度图通常n=8,P(x)为像素值x的累加概率。

使用C++实现上述算法:

void my_equalizeHist(Mat src, Mat &dst){  dst = Mat::zeros(src.size(), CV_8UC1);  float hist_tmp[256] = {0.0};  for(int i = 0; i < src.rows; i++)  {    uchar *p = src.ptr(i);    for(int j = 0; j < src.cols; j++)    {      hist_tmp[p[j]] += 1.0;   //统计直方图    }  }  uchar lutt[256] = {0};  float imgsize = 1.0/(src.rows*src.cols);  hist_tmp[0] = hist_tmp[0]*imgsize;  lutt[0] = uchar(hist_tmp[0]*255 + 0.5);    for(int i = 1; i 256; i++)      {    hist_tmp[i] = hist_tmp[i-1] + hist_tmp[i]*imgsize;    lutt[i] = uchar(hist_tmp[i]*255 + 0.5);   //计算查找表,加0.5是为了四舍五入取整  }  dst = Mat::zeros(src.size(), CV_8UC1);  for(int i = 0; i < dst.rows; i++)  {    uchar *p_s = src.ptr(i);    uchar *p_d = dst.ptr(i);    for(int j = 0; j < dst.cols; j++)    {      p_d [j] = lutt[p_s [j]];   //像素重映射    }  }}

运行上述代码,对Lena图像进行直方图均衡化,结果如下,可以看到直方图均衡化之后,图像对比度增强了,统计直方图分布也更加均匀。

原图

直方图均衡化之后图像

原图的统计直方图

直方图均衡化之后的统计直方图

4. 观察上方H(x)的计算公式,P(x)的取值范围是0~1,H(x)与P(x)具有线性关系。为了进一步提升图像对比度,可以对P(x)作一个非线性的S型变换T(P(x)),并保证变换之后T(P(x))的取值范围还在0~1之间。本文分别构造了T1和T2这两个非线性函数用于非线性变换,同时我们可以把原本H(x)与P(x)的线性关系看成函数T0,于是有以下三种变换:

画出以上三种变换函数在0~1区间的曲线如下图所示,可以看到T1和T2的曲线都是S型。

从而H(x)与P(x)有三种映射关系:

增加非线性变换的代码实现与上述代码大同小异:

#define PI 3.14159inline float T1(float x){  return (0.5*(sin(PI*x-PI/2)+1));}inline float T2(float x){  float xx = 3 - x*6;  return (1.0/(1+exp(xx)));}void my_equalizeHist(Mat src, Mat &dst){  dst = Mat::zeros(src.size(), CV_8UC1);  float hist_tmp[256] = {0.0};  for(int i = 0; i < src.rows; i++)  {    uchar *p = src.ptr(i);    for(int j = 0; j < src.cols; j++)    {      hist_tmp[p[j]] += 1.0;   //统计直方图    }  }  uchar lutt[256] = {0};  float imgsize = 1.0/(src.rows*src.cols);  hist_tmp[0] = hist_tmp[0]*imgsize;  //lutt[0] = uchar(hist_tmp[0]*255 + 0.5);       //T0  //lutt[0] = uchar(T1(hist_tmp[0])*255 + 0.5);   //T1  lutt[0] = uchar(T2(hist_tmp[0])*255 + 0.5);     //T2    for(int i = 1; i < 256; i++)    //计算累加概率  {    hist_tmp[i] = hist_tmp[i-1] + hist_tmp[i]*imgsize;        //计算查找表,加0.5是为了四舍五入取整      //lutt[i] = uchar(hist_tmp[i]*255 + 0.5);       //T0    //lutt[i] = uchar(T1(hist_tmp[i])*255 + 0.5);   //T1    lutt[i] = uchar(T2(hist_tmp[i])*255 + 0.5);     //T2  }  dst = Mat::zeros(src.size(), CV_8UC1);  for(int i = 0; i < dst.rows; i++)  {    uchar *p_s = src.ptr(i);    uchar *p_d = dst.ptr(i);    for(int j = 0; j < dst.cols; j++)    {      p_d [j] = lutt[p_s [j]];   //像素重映射    }  }}

分别选择T0、T1和T2变换,运行以上代码,得到的结果如下,可以看到使用T1和T2变换得到结果的对比度相对于T0有所提高。

T0

T1

T2

5. 上述直方图均衡算法还存在一个问题,就是当图像的0像素值占很大比例时,从起始的0像素值的累加概率就很大了,导致后面的1~255像素值的累加概率均变得很大,从而造成直方图均衡化之后的像素值都偏大。比如下图,可以看到直方图均衡化之后图像变白了,严重失真。

原图

直方图均衡化之后的图像

为解决上述问题,我们可以把0像素值排除在外,也即0像素值不做处理,从像素值1开始计算累加概率,同时计算累加概率时使用到的图像总像素数应减去0像素值的像素数。代码如下:

void my_equalizeHist(Mat src, Mat &dst){  dst = Mat::zeros(src.size(), CV_8UC1);  float hist_tmp[256] = {0.0};  for(int i = 0; i < src.rows; i++)  {    uchar *p = src.ptr(i);    for(int j = 0; j < src.cols; j++)    {      hist_tmp[p[j]] += 1.0;   //统计直方图    }  }  uchar lutt[256] = {0};  //总像素数减去0像素值的像素数  float imgsize = 1.0/(src.rows*src.cols - hist_tmp[0]);     hist_tmp[1] = hist_tmp[1]*imgsize;  //从像素值1开始计算累加概率  //lutt[1] = uchar(hist_tmp[1]*255 + 0.5);  //lutt[1] = uchar(T1(hist_tmp[1])*255 + 0.5);  lutt[1] = uchar(T2(hist_tmp[1])*255 + 0.5);    dst = Mat::zeros(src.size(), CV_8UC1);  for(int i = 2; i < 256; i++)    //计算累加概率  {    hist_tmp[i] = hist_tmp[i-1] + hist_tmp[i]*imgsize;    //lutt[i] = uchar(hist_tmp[i]*255 + 0.5);    //lutt[i] = uchar(T1(hist_tmp[i])*255 + 0.5);    lutt[i] = uchar(T2(hist_tmp[i])*255 + 0.5);  }    dst = Mat::zeros(src.size(), CV_8UC1);  for(int i = 0; i < dst.rows; i++)  {    uchar *p_s = src.ptr(i);    uchar *p_d = dst.ptr(i);    for(int j = 0; j < dst.cols; j++)    {      p_d [j] = lutt[p_s [j]];   //像素重映射    }  }}

运行上述代码,得到结果,可以看到直方图均衡化之后,像素值不会再有整体偏高的问题。

不调用python函数实现直方图均衡化_数字图像处理之直方图均衡化相关推荐

  1. 数字图像处理与python实现 岳亚伟_数字图像处理与Python实现

    01 数字图像处理基础知识 1.1 数字图像简介 1.1.1 数字图像处理的目的 1.1.2 数字图像处理的应用 1.1.3 数字图像处理特点 1.1.4 常见的数字图像处理方法 1.2 图像采样和量 ...

  2. python中execute函数_在excel中调用python函数

    效果: 通过excel引用在py文件中写好的load_settle()函数,可以快捷的获取对应的历史结算价. 使用方法: 1.首先安装office,我用的是2016版本. 2.安装python,推荐使 ...

  3. python绘制灰度图片直方图-python数字图像处理实现直方图与均衡化

    在图像处理中,直方图是非常重要,也是非常有用的一个处理要素. 在skimage库中对直方图的处理,是放在exposure这个模块中. 1.计算直方图 函数:skimage.exposure.histo ...

  4. VS2017 QT/C++ 调用python函数传图像

    原文:VS2019 C++ 调用python函数/类对象的方法_ 蜗牛在听雨的博客-CSDN博客_c++调用python函数 1.c++调用python类(传图像参数) ,编译出错,解决方法: 因为需 ...

  5. c调用python脚本如何获取结果_使用C++调用Python代码的方法详解

    一.配置python环境问题 1.首先安装Python(版本无所谓),安装的时候选的添加python路径到环境变量中 安装之后的文件夹如下所示: 2.在VS中配置环境和库 右击项目->属性-&g ...

  6. C++调用Python函数

    From: http://www.flatws.cn/article/program/c/2010-08-24/9677.html Python代码在实现某些功能的时候非常方便,如果能够将Python ...

  7. C++和Python混合编程:C++调用Python函数

    文章目录 一.C++直接运行python代码的控制台Demo 二.环境配置 三.C++调用Python函数 C++传入Python的参数格式转换 C++调用Python[有参有返回值]函数 C++运行 ...

  8. Excel单元格使用xlwings包调用python函数的公式,截取子网页(标题)的试验 问题求助CSDN

    Excel单元格使用xlwings包调用python函数的公式,截取子网页(标题)的试验 问题求助CSDN Python 环境:python3.7 的conda上的py3环境 Excel 2010 E ...

  9. C++回调函数中调用Python函数出现的死锁问题调试及解决

    一.查找死锁原因: 1.使用gdb exe指令进入gdb命令行,再输入r运行可执行文件 gdb /home/sdhm/catkin_ws/devel/lib/gpd_ros/gpd_server GN ...

  10. python函数默认参数位置_二十二、Python函数参数类型(位置、关键字、默认、不定长参数)...

    调用函数时可使用的参数类型 在调用Python函数时可使用的参数类型主要有以下几种: 必要参数(位置参数) 关键字参数 默认参数 不定长参数 必要参数(位置参数) 在Python中, 必要参数必须以正 ...

最新文章

  1. Android 属性动画(Property Animation) ObjectAnimator的介绍
  2. ESXi主机遗忘密码重置密码
  3. Docker容器启动自动化脚本(五)
  4. 从一个小demo开始,体验“API经济”的大魅力
  5. 没有什么是日本牛郎店做不到的......
  6. 常州模拟赛d4t1 立方体
  7. 复旦教授:不打不骂不罚是培养不出优秀孩子的!值得一看
  8. 安卓也是html写的么,css能判断手机是安卓还是ios吗?
  9. matlab2013和2014,64位机器MATLAB2013b和MATLAB2014a没有LCC编译器,怎么安装它呢?
  10. 【ELT.ZIP】OpenHarmony啃论文俱乐部—数据密集型应用内存压缩
  11. Linux redhat 5.7 安装 Teamviewer7
  12. 财务会计上的凭证冲销和SAP中的凭证冲销(红冲、蓝冲)
  13. uni-app背景图片 background-image,支持 base64 格式图片、支持网络路径图片、本地路径背景图片
  14. 如何使用Flashfxp上传下载文件
  15. 写入iCloud在模拟器和真机上失败的解决办法
  16. wannier拟合能带总是拟合不上_科学网-Wannier90输入文件中num_wann, num_bands, 和energy window等参数设置规则-李云海的博文...
  17. windows10删除EFI分区(绝对安全)
  18. 算法笔记_203:第四届蓝桥杯软件类决赛真题(C语言B组)
  19. UE4 FlipFlop的使用
  20. Oracle索引详解(索引的原理,创建索引,删除索引,修改索引等)

热门文章

  1. Linux 上关于iptables
  2. 《Oracle从入门到精通》读书笔记第三章 SQL*PLUS命令
  3. 如何为ip v6设定子网
  4. 动态SQL和PL/SQL的EXECUTE IMMEDIATE选项
  5. Java计算接口请求时间
  6. postman发送json格式的post请求
  7. Could not autowire field: XXXXX.
  8. delphi 防止程序双开 更好的 Best!
  9. Spring Boot Web 开发注解篇
  10. 谷歌被曝出滥用苹果后门收集用户数据