目录

  • 概念
    • 直方图定义
    • 直方图均衡化
      • 为什么要选用累积分布函数
      • 如何运用累积分布函数使得直方图均衡化
  • C++ 源码
    • 直方图均衡化
    • 绘制直方图
    • 主函数
  • 效果
  • 完整源码

平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3


本文综合自直方图计算和程序员-图哥——图像处理之直方图均衡化及C++实现

概念

直方图定义

直方图均衡化


直方图均衡化的作用是图像增强。

有两个问题比较难懂,一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布。

为什么要选用累积分布函数

均衡化过程中,必须要保证两个条件:
①像素无论怎么映射,一定要保证原来的大小关系不变,较亮的区域,依旧是较亮的,较暗依旧暗,只是对比度增大,绝对不能明暗颠倒;
②如果是八位图像,那么像素映射函数的值域应在0和255之间的,不能越界。

综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。

如何运用累积分布函数使得直方图均衡化

直方图均衡化过程中,映射方法是

其中,n是图像中像素的总和,nkn_knk​是当前灰度级的像素个数,L是图像中可能的灰度级总数。

C++ 源码

直方图均衡化

直方图均衡化的代码实现有以下几个步骤:

遍历全图,先统计每个灰度级下的像素点个数(为此我们开辟了256大小的数组);
计算每个灰度级的像素点占总像素的点的比例;
按照第二步求出的比例重新计算每个灰度级下的新的灰度值,即均衡化;
依照新的灰度值表遍历更新图像的灰度值。

Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}

绘制直方图

Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}

主函数

图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\\”

int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

效果




完整源码

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

【机器视觉学习笔记】直方图的绘制及直方图均衡化(C++)相关推荐

  1. matplotlib学习笔记 - 散点图、条形图和直方图

    Matplotlib 学习笔记 - 散点图.条形图和直方图 散点图 from matplotlib import pyplot as plt from matplotlib import font_m ...

  2. 机器视觉学习笔记(4)——单目摄像机标定参数说明

    机器视觉学习笔记(4)--单目摄像机标定参数说明 标签: 机器视觉 1.针孔摄像机模型 在介绍摄像机标定参数之前,需要先简单说一下针孔摄像机的原理.投影平面到小孔的距离为焦距f,物体到小孔的距离为Z, ...

  3. 图像归一化处理 相同像素_图像处理学习笔记(九)——灰度直方图及其均衡化(理论篇)...

    本篇介绍灰度直方图和均衡化,也是属于图像灰度变换得范畴: 一.直方图 直方图:表示图像中具有某种灰度级的像素的个数,反映了图像中每种灰度出现的频率.是图像最基本的统计特征. 横坐标:灰度级: 纵坐标: ...

  4. OpenCV-python学习笔记(三)——histograms直方图

    文章目录 histograms直方图 1 使用opencv来计算直方图 2 grayscale histograms灰度直方图 3 color histograms彩色直方图 4 彩色二维直方图 5 ...

  5. OpenCV学习笔记(九)之直方图均衡化(图像增强)

    图像直方图 1.图像直方图介绍   图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数.可以借助观察该直方图了解需要如何调整亮度分布.这种 ...

  6. #大创学习笔记#part1宫颈癌细胞图像分割——直方图最亮谷底阈值确定法提取细胞前景(2)

    今天终于开学了,也意味着我终于能够静下心去做大创的项目,汲取新的知识. 数字图像处理作为专业选修课,学分重,任务多.但是我也从中了解到了更多关于图像处理的知识,内心也充满激动,哈哈哈,怕是上课的时候在 ...

  7. #大创学习笔记#part1宫颈癌细胞图像分割——直方图最亮谷底阈值确定法提取细胞前景

    尝试着做一个工程日记,也想把自己从项目中所学到的知识经验记录下来.而且网上的资源繁多复杂,方法也是各式各样,难免让人眼花缭乱,整理以下思路,供大家参考. 在研究很多篇文献后,我选用了一篇硕士论文中的研 ...

  8. 机器视觉学习笔记(七)——灰度分析与变换(基于LabVIEW)

    图像分析:将图像的像素灰度统计和测量技术结合,是机器可以理解图像内容,并提取特征信息以实现智能检测目的的学科,侧重于对图像内容的分析,解释和识别. 图像灰度分析是图像中最基本的内容:直方图是最基本的图 ...

  9. python图形设置_python学习笔记——基本图形绘制

    1.python蟒蛇python蟒蛇代码 #PythonDraw.py import turtle turtle.setup(650,350,200,200)# 设置画布的长.宽.起点 turtle. ...

最新文章

  1. HTML5 Geolocation
  2. 10.07-Idea的使用
  3. HTML5学习笔记(十四):变量作用域
  4. 剑指offer之用链表实现栈(带头节点)
  5. Linux驱动如何在不同版本上快速迭代升级
  6. android 字符串替换_Android开发之旅:android架构
  7. contentProvider 内容提供者
  8. newifimini出厂固件_新路由newifi固件
  9. kubernetes入门之Downward API
  10. (每日一练c++)统计某一单科成绩各分数段的分布人数
  11. 弘辽科技:美团和阿里的恩爱情仇
  12. arduino点阵声音频谱_Arduino实现32分频音频频谱显示器
  13. 红帽与Cloudera结成大数据联盟 释放企业级Hadoop潜能
  14. TFDS中检测算法的应用
  15. 一般业务系统的数据字典表结构
  16. Datatable 转换成Json
  17. 144hz和60hz测试软件,关于显示器,60hz与144hz,以个人角度,给大家分享心得…
  18. Installation silently blocked for package name fix
  19. 软考初级程序员上午单选题(10)
  20. python读取oracle数据库数据库_Python读写Oracle数据库

热门文章

  1. 匹配字符串-好技能-正则表达式
  2. 2月份Web服务器份额:排名不变 仅Microsoft份额上扬
  3. vue取数据第一个数据_我作为数据科学家的第一个月
  4. leetcode 1006. 笨阶乘
  5. 无服务器架构_如何开始使用无服务器架构
  6. git 短写设置_如何在短短几分钟内设置一个Git客户端
  7. react 组件引用组件_React Elements VS React组件
  8. 力扣——k个一组翻转链表
  9. hibernate 注解
  10. 启用或禁用对 Exchange Server 中的邮箱的 POP3 或 IMAP4 访问