背景

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

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. JavaWeb之文件上传、下载

    时间:2016-12-17 18:07 --文件上传概述上传不能使用BaseServlet1.文件上传的作用    例如网络硬盘,就是用来上传和下载文件的.2.文件上传对表单的限制    1)必须使用 ...

  2. C++、Java、Python、Linux、Go、前端、算法,慕课资料分享

    C++.Java.Python.Linux.Go.前端.算法,慕课资料分享 微信公众号:大道同行JAVA 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 又见面了.废话不多说,最近多了一些在 ...

  3. 哲学家就餐问题-Java语言实现死锁避免

    哲学家就餐问题-Java语言实现死锁避免 我死锁预防是至少破坏死锁产生的四个必要条件之一,带来的问题就是系统资源利用率低且不符合开发习惯,而死锁避免不是事先釆取某种限制措施破坏死锁的必要条件,只是注意 ...

  4. JS_DOM操作之操作标签

    <标签名 属性1="属性值1" 属性2="属性值2"-->文本</标签名> 1 - 文本操作 <div class="c ...

  5. Kubernetes环境Traefik部署与应用

    本作品由Galen Suen采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可.由原作者转载自个人站点. 概述 本文用于整理基于Kubernetes环境的Traefik部署与应用, ...

  6. 内核软中断之tasklet机制

    1. 软中断IRQ简介 软中断(SoftIRQ)是内核提供的一种基于中断的延时机制, Linux内核定义的软中断有以下几种: enum { HI_SOFTIRQ=0, /*高优先级的tasklet*/ ...

  7. CSS002. 字体穿透蒙层(用img设置字体的color)

    之前在逛Apple Store时看到了下面的UI: 交互图标非常圆滑上手也很舒服,虽然背景底色本就是白底,但是只依赖css能不能使  "+" 穿透背景看到底色 ? 大致思路如下: ...

  8. log4J日志输出修改

    1. log4j.rootLogger=DEBUG,INFO, console, log, error ###Console ### log4j.appender.console = org.apac ...

  9. CSS滤镜让图片模糊(毛玻璃效果)

    CSS代码: .blur { filter: url(blur.svg#blur); /* FireFox, Chrome, Opera */ -webkit-filter: blur(10px); ...

  10. RMI源码调试

    看RMI漏洞时候,对其漏洞原理并不是很理解,所以简单调试了下源码加强下漏洞理解 由于要调试到RegistryImpl_Stub这种动态类,刚开始用的源码版本是JDK8u141,后来发现源码有些地方进行 ...