背景

图像的直方图是衡量图像像素分布的一种方式,可以通过分析像素分布,使用直方图均衡化对图像进行优化,让图像变的清晰。

opencv官方对图像直方图的定义如下:

  • 直方图是图像中像素强度分布的图形表达方式.
  • 它统计了每一个强度值所具有的像素个数.

一、直方图计算的原理

一副图像实际上就是一个数字矩阵。

3x3的灰度图像由9个像素组成,每个像素都取值0-255中的一个值,0表示黑色,255表示白色,中间值是介于黑色和白色之间的灰度值。

如下以一个高度为3,宽度为3的图片为例说明直方图的计算。

  • 定义一个255大小的数组,用于保存灰度值出现的次数
  • 遍历图像的每一个元素,将像素的灰度值出现的次数统计到对应的灰度次数中
  • 将灰度值次数统计数组进行归一化处理(归一化到0-255范围内,便于绘图使用)
  • 将归一化的灰度次数进行绘图展示

如下图是计算直方图的过程。

二、直方图计算步骤

根据直方图计算的原理,如下我们就开始动手写一个计算图像直方图代码实现。

1. 加载图像

加载图像,并显示

    cv::Mat rawImage = cv::imread("demo1/leopard2.png", cv::IMREAD_ANYCOLOR);
cv::imshow("rawImage", rawImage);

图像显示图像(我喜欢的那个小豹子)

2. 定义统计图像三个通道灰度值出现次数和归一化数的数组

定义并初始化次数数组,按照灰度值255,用于统计每个像素灰度值出现的次数。

        int histSize = 255;
int histValues[3][255] = {};
int histNormalizeValues[3][255] = {};
for (int k = 0; k < histSize; ++k) {
histValues[0][k] = 0;
histValues[1][k] = 0;
histValues[2][k] = 0;
histNormalizeValues[0][k] = 0;
histNormalizeValues[1][k] = 0;
histNormalizeValues[2][k] = 0;
}

3. 遍历图像,计算三个通道灰度值出现的次数

彩色图像由BGR三个通道构成,分别计算统计这三个通道的灰度值次数

       cv::Vec3b rgbPixel;
// 遍历图像,统计BGR三个通道的图像的灰度值出现的次数
for (int i = 0; i < rgbImage.rows; ++i) {
for (int j = 0; j < rgbImage.cols; ++j) {
// B G R
rgbPixel = rgbImage.at<cv::Vec3b>(i, j);
histValues[2][rgbPixel[2]] += 1;
histValues[1][rgbPixel[1]] += 1;
histValues[0][rgbPixel[0]] += 1;
}
}

4. 将上一步图像灰度值次数归一化到0-255之间

归一化方法的算法见之前的文章 https://www.cnblogs.com/voipman/p/5046153.html

        // 把如上的统计值归一化到0-255范围内
calcNormalize(histValues[0], histNormalizeValues[0]);
calcNormalize(histValues[1], histNormalizeValues[1]);
calcNormalize(histValues[2], histNormalizeValues[2]);

归一化代码实现

    /**
* 计算一个数组的归一化,此处归一化到0-255之间
* @param srcValues
* @param dstValues
*/
void calcNormalize(int srcValues[255], int dstValues[255]) {
int minValue = srcValues[0];
int maxValue = srcValues[0]; for (int i = 1; i < 255; ++i) {
if (minValue > srcValues[i]) {
minValue = srcValues[i];
}
if (maxValue < srcValues[i]) {
maxValue = srcValues[i];
}
}
int minMaxDiff = maxValue - minValue;
for (int j = 0; j < 255; ++j) {
dstValues[j] = static_cast<int>((float)(srcValues[j] - minValue) / (float)minMaxDiff * 255.);
}
}

5. 绘制直方图到页面

如下划线代码逻辑是画出3条线,分别是蓝绿红三条,每一条线连接前后两个点,依次连接0-254点形成对应的线。

        // 创建直方图画布
int hist_w = 400; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize ); cv::Mat histImage( hist_w, hist_h, CV_8UC3, cv::Scalar( 255,255,255) );
// 把三个通道的直方图归一化数据绘制在直方图上
for (int i = 1; i < histSize; ++i) {
cv::line(histImage,
cv::Point(bin_w * (i-1), hist_h - cvRound(histNormalizeValues[0][i-1])),
cv::Point(bin_w * (i), hist_h - cvRound(histNormalizeValues[0][i])),
cv::Scalar(0, 0, 255), 2,cv::LINE_AA, 0);
cv::line(histImage,
cv::Point(bin_w * (i-1), hist_h - cvRound(histNormalizeValues[1][i-1])),
cv::Point(bin_w * (i), hist_h - cvRound(histNormalizeValues[1][i])),
cv::Scalar(0, 255, 0), 2,cv::LINE_AA, 0);
cv::line(histImage,
cv::Point(bin_w * (i-1), hist_h - cvRound(histNormalizeValues[2][i-1])),
cv::Point(bin_w * (i), hist_h - cvRound(histNormalizeValues[2][i])),
cv::Scalar(255, 0, 0), 2,cv::LINE_AA, 0);
}
cv::imshow("histImage", histImage);

绘图中的绘线逻辑如下图中的绿线线段所示(连接前后两个点形成对应的线段):

6. 绘制直方图显示

直方图结果解析和说明:

  • 从这个直方图可以看出原始图像三个通道的数据都比较集中
  • 红色通道的数据集中在中间130左右,太黑和太白的数据比较少。
  • 绿色通道的数据集中在180左右,两边数据比较少。
  • 蓝色通道的数据集中在210作用的数值内,黑色的数据很少。

图像优化

使用直方图均衡化算法对图像进行均衡处理

    void EqualizeHist(cv::Mat &rgbImage) {
std::vector<cv::Mat> rgbImages;
cv::split(rgbImage, rgbImages);
/// 应用直方图均衡化 cv::Mat dstR, dstG, dstB;
equalizeHist(rgbImages[0], dstB);
equalizeHist(rgbImages[1], dstG);
equalizeHist(rgbImages[2], dstR); std::vector<cv::Mat> grayHistImages;
grayHistImages.push_back(dstB);
grayHistImages.push_back(dstG);
grayHistImages.push_back(dstR);
cv::merge(grayHistImages, rgbImage);
}

  

对图像做了直方图均衡化处理后的效果如下:

图像分析:

  • 图像看起来黑白分明,小豹子图像很清晰。

经过直方图均衡化处理后的图像,重新计算直方图,观察灰度值分布

图像分析:

  • 均衡化后的直方图均匀的分布在0-255之间。

OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图,步骤如下

  • cv::split拆分图像到多个通道
  • 使用计算直方图函数 calcHist 计算图像的直方图
  • 使用函数 cv::normalize 归一化数组
  • 使用cv::line绘制直方图

参考材料:

opencv直方图均衡化处理

opencv直方图计算

如下完整代码见 https://github.com/gityf/img-video/blob/master/opencv/hist.hpp

done.

祝玩的开心~

【图像处理】基于OpenCV实现图像直方图的原理的更多相关文章

  1. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  2. Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  3. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

  4. Java基于opencv实现图像数字识别(二)—基本流程

    Java基于opencv实现图像数字识别(二)-基本流程 做一个项目之前呢,我们应该有一个总体把握,或者是进度条:来一步步的督促着我们来完成这个项目,在我们正式开始前呢,我们先讨论下流程. 我做的主要 ...

  5. Java基于opencv实现图像数字识别(一)

    Java基于opencv实现图像数字识别(一) 最近分到了一个任务,要做数字识别,我分配到的任务是把数字一个个的分开:当时一脸懵逼,直接百度java如何分割图片中的数字,然后就百度到了用Buffere ...

  6. Python+OpenCV图像处理(八)—— 图像直方图

    直方图简介:图像的直方图是用来表现图像中亮度分布的直方图,给出的是图像中某个亮度或者某个范围亮度下共有几个像素.还不明白?就是统计一幅图某个亮度像素数量.比如对于灰度值12,一幅图里面有2000 个像 ...

  7. 为基于OpenCV的图像处理程序编写界面—关于QT\MFC\CSharp的选择以及GOCW的介绍

            基于OpenCV编写图像处理项目,除了算法以外,比较重要一个问题就是界面设计问题.对于c++语系的程序员来说,一般来说有QT/MFC两种考虑.QT的确功能强大,特别是QML编写andr ...

  8. 【4opencv】为基于OpenCV的图像处理程序编写界面—关于QT\MFC\CSharp的选择以及GOCW的介绍

            基于OpenCV编写图像处理项目,除了算法以外,比较重要一个问题就是界面设计问题.对于c++语系的程序员来说,一般来说有QT/MFC两种考虑.QT的确功能强大,特别是QML编写andr ...

  9. 深入学习OpenCV中图像灰度化原理,图像相似度的算法

    最近一段时间学习并做的都是对图像进行处理,其实自己也是新手,各种尝试,所以我这个门外汉想总结一下自己学习的东西,图像处理的流程.但是动起笔来想总结,一下却不知道自己要写什么,那就把自己做过的相似图片搜 ...

随机推荐

  1. 二.Go微服务--令牌桶

    1. 令牌桶 1.1 原理 我们以 r/s 的速度向桶内放置令牌,桶的容量为 b , 如果桶满了令牌将会丢弃 当请求到达时,我们向桶内获取令牌,如果令牌足够,我们就通过转发请求 如果桶内的令牌数量不够 ...

  2. Java反射的浅显理解

    一.回顾反射相关的知识 1.在xml文件中使用反射的好处: 1)代码更加灵活,后期维护只需要修改配置文件即可 · 初学者一般习惯于在代码本身上直接修改,后期也可以修改配置文件达到相同的目的 · 修改配 ...

  3. Python - 面向对象编程 - 三大特性之继承

    继承 继承也是面向对象编程三大特性之一 继承是类与类的一种关系 定义一个新的 class 时,可以从某个现有的 class 继承 新的 class 类就叫子类(Subclass) 被继承的类一般称为父 ...

  4. Ansible基础使用

    原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 Ansible命令用法 1.1 免密钥 1.2 Ad-Hoc基础命令 1.3 ...

  5. 类的基础语法阅读【Python3.8官网文档】

    英文官方文档: https://docs.python.org/3.8/tutorial/classes.html 中文官方文档: https://docs.python.org/zh-cn/3.8/ ...

  6. MFC中L, _T(),TEXT,_TEXT区别以及含义

    字符串前面加L表示该字符串是Unicode字符串. _T是一个宏,如果项目使用了Unicode字符集(定义了UNICODE宏),则自动在字符串前面加上L,否则字符串不变.因此,Visual C++里边 ...

  7. SpringBoot - 搭建静态资源存储服务器

    目录 前言 环境 实现效果 具体实现 文件上传 配置类 上传接口 上传实现 辅助类 实体 上传测试 文件访问 配置类 项目源码 前言 记录下SpringBoot下静态资源存储服务器的搭建. 环境 wi ...

  8. 免费 CDN 玩法 —— 将整个网站打包成一个图片文件

    资源合并 前端开发者都知道,过多的请求对性能影响很大.而且有些 CDN 不仅按流量收费,请求数也收费,如果网页里有大量小文件,显然不划算. 为此不少开发者将零碎的小文件进行合并优化,例如 JS/CSS ...

  9. SQL-DELETE触发器练习

    &练习一 如下所示三张表( student,grade,student_updata_before ): student表 grade表 Student_update_before表 # 触发 ...

  10. Linux上安装服务器监视工具,名为Scout_Realtime。

    如何从浏览器监视Linux服务器和进程指标 在服务器上安装Ruby 1.9.3+ sudo yum -y install rubygems-devel 在Linux系统上安装了Ruby之后,现在可以使 ...