Goal

在本教程中,您将学习如何:

使用 OpenCV 函数 cv::split图像划分为其对应的平面。

使用 OpenCV 函数 cv::calcHist 计算图像数组直方图

使用函数 cv::normalize 规范化数组

笔记

在上一个教程(直方图均衡)中,我们讨论了一种特殊的直方图,称为图像直方图。 现在我们将考虑它的更一般的概念。 继续阅读!

What are histograms?

直方图是收集的数据计数,组织成一组预定义的 bin

当我们说数据时,我们并没有将其限制为强度值(正如我们在前面的教程直方图均衡中看到的那样)。 收集的数据可以是您认为对描述图像有用的任何特征

让我们看一个例子。 想象一个矩阵包含图像的信息(即 0-255 范围内的强度):

如果我们想以有组织的方式统计这些数据会发生什么? 由于我们知道这种情况下的信息值范围是 256 个值,因此我们可以将我们的范围划分为子部分subparts(称为 bin),例如:

[0,255]=[0,15]∪[16,31]∪....∪[240,255]range=bin1∪bin2∪....∪binn=15

我们可以计算落在每个 bini 范围内的像素数。 将其应用于上面的示例,我们得到下面的图像(轴 x 表示 bin,轴 y 表示每个 bin 中的像素数)。

这只是直方图如何工作以及为什么有用的一个简单示例。 直方图不仅可以计算颜色强度,还可以计算我们想要测量的任何图像特征(即梯度方向等)。

让我们识别直方图的某些部分:

dims:要收集数据的参数数量。 在我们的示例中, dims = 1 因为我们只计算每个像素的强度值(在灰度图像中)。

bins:是每个dim中的细分数。 在我们的示例中,bins = 16

range要测量的值的限制。 在这种情况下:range = [0,255]

如果你想计算两个特征怎么办? 在这种情况下,您生成的直方图将是一个 3D 图(其中 x 和 y 将是每个特征的 binx 和 binyz 将是 (binx,biny) 的每个组合的计数数。这同样适用于更多特征 (当然它变得更棘手)。

What OpenCV offers you

出于简单的目的,OpenCV 实现了函数 cv::calcHist ,它计算一组数组(通常是图像或图像平面)的直方图。 它可以操作多达 32 个维度。 我们将在下面的代码中看到它!

Code

这个程序有什么作用?

加载图像

使用函数 cv::split图像拆分为 R、G 和 B 平面

通过调用函数 cv::calcHist 计算每个 1 通道平面的直方图

在窗口中绘制三个直方图

可下载代码:点击这里https://github.com/opencv/opencv/tree/4.x/samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp

代码一览:

/*** @function calcHist_Demo.cpp* @brief 计算直方图Demo code to use the function calcHist* @author*/#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>using namespace std;
using namespace cv;/*** @function main*/
int main(int argc, char** argv)
{//! [加载图像]CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );if( src.empty() ){return EXIT_FAILURE;}//! [Load image]//! [将图像分成 3 个平面(B、G 和 R)]vector<Mat> bgr_planes;split( src, bgr_planes );//! [Separate the image in 3 places ( B, G and R )]//! [确定 bin 数量]int histSize = 256;//! [Establish the number of bins]//! [设置范围(用于 B、G、R))]float range[] = { 0, 256 }; //上边界是排除在外的const float* histRange = { range };//! [Set the ranges ( for B,G,R) )]//! [设置直方图参数]bool uniform = true, accumulate = false;//! [Set histogram param]//! [计算直方图]Mat b_hist, g_hist, r_hist;calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );//! [Compute the histograms]//! [绘制 B、G 和 R 的直方图]int hist_w = 512, hist_h = 400;int bin_w = cvRound( (double) hist_w/histSize );Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );//黑色背景//! [Draw the histograms for B, G and R]//! [将结果归一化为 ( 0, histImage.rows )]normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );//! [Normalize the result to ( 0, histImage.rows )]//! [为每个通道绘制曲线]for( int i = 1; i < histSize; i++ ){line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),Scalar( 255, 0, 0), 2, 8, 0  );line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),Scalar( 0, 255, 0), 2, 8, 0  );line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),Scalar( 0, 0, 255), 2, 8, 0  );}//! [Draw for each channel]//! [显示]imshow("Source image", src );imshow("calcHist Demo", histImage );waitKey();//! [Display]return EXIT_SUCCESS;
}

Explanation

  • 加载源图像
    CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );if( src.empty() ){return EXIT_FAILURE;}
  • 在其三个 R、G 和 B 平面中分离源图像。 为此,我们使用 OpenCV 函数  cv::split :
    vector<Mat> bgr_planes;// Mat 的向量split( src, bgr_planes );

我们的输入是要分割的图像(这种情况下是三个通道),输出是 Mat 的向量

现在我们准备开始为每个平面配置直方图。 由于我们正在使用 B、G 和 R 平面,我们知道我们的值将在 [0,255] 区间内

确定 bin 的数量(5、10...):

int histSize = 256;
  • 设置值的范围(如我们所说,介于 0 和 255 之间)
float range[] = { 0, 256 }; //the upper boundary is exclusive
const float* histRange[] = { range };
  • 我们希望我们的 bin 具有相同的大小(统一)并在开始时清除直方图,因此:
bool uniform = true, accumulate = false;
  • 我们继续使用 OpenCV 函数  cv::calcHist :计算直方图:
    Mat b_hist, g_hist, r_hist;calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, histRange, uniform, accumulate );calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, histRange, uniform, accumulate );calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, histRange, uniform, accumulate );
  • 参数是 (C++ code):

&bgr_planes[0]: 源数组

1:源数组的数量(在这种情况下,我们使用 1。我们也可以在此处输入数组列表

0:测量的通道dim)。 在这种情况下,它只是强度(每个array 都是单通道),所以我们只写 0。

Mat():要在源数组上使用的掩码(零表示要忽略的像素)。 如果未定义,则不使用

b_hist:将存储直方图的 Mat 对象

1:直方图维数。

histSize:每个使用的维度的 bin 数量

histRange:每个维度要测量的值的范围

uniform and accumulate::bin 大小相同,柱状图在开始时被清除。

  • 创建图像以显示直方图:
    int hist_w = 512, hist_h = 400;int bin_w = cvRound( (double) hist_w/histSize );

Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );

  • 请注意,在绘制之前,我们首先对直方图进行 cv::normalize,使其值落在输入参数指示的范围内:
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
  • 此函数接收这些参数(C++ 代码):

b_hist:输入数组

b_hist:输出归一化数组(可以相同)

0histImage.rows:对于这个例子,它们是标准化 r_hist 值的下限和上限

NORM_MINMAX:表示归一化类型的参数(如上所述,它调整之前设置的两个限制之间的值)

-1:表示输出的归一化数组将与输入的类型相同

Mat():可选掩码

  • 观察以访问bin(在本例中为一维直方图):
    for( int i = 1; i < histSize; i++ ){   //直线line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),Scalar( 255, 0, 0), 2, 8, 0  );line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),Scalar( 0, 255, 0), 2, 8, 0  );line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),Scalar( 0, 0, 255), 2, 8, 0  );}

我们使用表达式(C++ 代码):

b_hist.at<float>(i)

其中 i 表示维度值。 如果它是一个二维直方图,我们会使用类似的东西:

b_hist.at<float>( i, j )

最后我们显示我们的直方图并等待用户退出:

imshow("Source image", src );
imshow("calcHist Demo", histImage );
waitKey();

Result

  1. 使用如下图所示的图像作为输入参数:

  1. 生成以下直方图:

【opencv450 Image Processing】Histogram Calculation直方图计算相关推荐

  1. OpenCV(十九)直方图(直方图计算、掩膜、均衡化、自适应均衡化)

    目录 一.基础理论 1.原理及作用 2.专业术语 二.直方图计算 函数介绍: 1.灰度图 代码: 效果: 2.彩色图 代码: 三.直方图掩膜的应用(mask) 1.基础理论 2.代码 3.效果 四.直 ...

  2. OpenCV之imgproc 模块. 图像处理(4)直方图均衡化 直方图计算 直方图对比 反向投影 模板匹配

    直方图均衡化 目标 在这个教程中你将学到: 什么是图像的直方图和为什么图像的直方图很有用 用OpenCV函数 equalizeHist 对图像进行直方图均衡化 原理 图像的直方图是什么? 直方图是图像 ...

  3. Win8 Metro(C#)数字图像处理--3.3图像直方图计算

    原文:Win8 Metro(C#)数字图像处理--3.3图像直方图计算 /// <summary>/// Get the array of histrgram./// </summa ...

  4. 【opencv学习笔记】025之直方图计算 - calcHist函数详解

    前言 如果你想了解更多有关于计算机视觉.OpenCV.机器学习.深度学习等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧! 1.calcHist函数是干什么滴? 这个问题嘛,看看标 ...

  5. 数字图像学笔记——4. 直方图计算、线性变换、对数变换、Gamma变换

    文章目录 灰度直方图(Gray Histogram) 直方图的计算方法 简单的图像转换方法 线性变换 / 图像翻转(Image Nagatives) 对数变换(Log Transformation) ...

  6. 详解为什么OpenCV的直方图计算函数calcHist()计算出的灰度值为255的像素个数为0

    在使用OpenCV的直方图计算函数calcHist()时,发现灰度值为255的像素个数总是为0. 哪怕图像中灰度值为255的像素个数不为0,使用OpenCV的直方图计算函数calcHist()计算出的 ...

  7. OpenCV-Python图像直方图计算calcHist函数详解、示例及图形呈现

    ☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 在前面几篇直方图相关的文章中介绍了直方图均衡.直方图匹配.局部直方图处理.基 ...

  8. 【QtOpenCV 直方图计算 split/calcHist/normalize】

    文章目录 前言 一.函数介绍 1 split 2 calcHist 3 normalize 二.演示 1.GUI 2.实现代码 总结 前言 越来越多的开发人员选择基于开源的Qt框架与OpenCV来实现 ...

  9. 【python 图片相似度】直方图计算图片相似度

    直方图能够描述一幅图像中颜色的全局分布,而且容易理解和实现,所以入门级的图像相似度计算都是使用它的. 直方图算法是对源图像与要筛选的图像进行直方图数据采集,对采集的各自图像直方图进行归一化再使用巴氏系 ...

最新文章

  1. 在图像旁边垂直对齐文字?
  2. 用vc++如何得到汉字的Unicode编码?
  3. 《一起》个人进展——Day06
  4. vscode 如何实时显示html文件?
  5. 小米登录协议分析_小米回应小米11充电头兼容问题
  6. linux shadow 时间,Linux Shadow-Password-HOWTO - 7. 将 Shadow Suite 放进来使用(1)
  7. 施一公获百万科学界大奖!科研大牛如何炼成?
  8. 云图说|小云妹带你揭秘数据复制服务DRS四大功能
  9. 《Scikit-Learn与TensorFlow机器学习实用指南》第10章 人工神经网络介绍
  10. C++语言string类介绍和示例
  11. 【Linux】ubuntu中怪异的vi编辑器
  12. 通过xmlhttp实现-报告归档
  13. TMS320F28335的特点
  14. goahead文件下载
  15. oracle 12c创建归档,oracle 12c 数据归档 即Using In-Database Archiving feature
  16. MySQL varchar类型可以存储多少个汉字
  17. Quasi-Dense Similarity Learning for Multiple Object Tracking 详细解读
  18. 地址池命令 思科理由_思科交换机DHCP功能和使用简介
  19. CF 678F Lena and Queries
  20. 关于谢尔宾斯基三角(Sierpinski)的讲解

热门文章

  1. 转:经验在组织管理中应扮演什么角色?
  2. JS实现小球移动(点击移动,点击停止)
  3. php输入文字不显示,ps写了文字为什么不显示 ps里输入文字不显示的四个原因及解决方法...
  4. 双目相机标定OpenCV源码讲解
  5. SpringBoot系列:Spring Boot定时任务Spring Schedule,springboot视频教程迅雷
  6. 罗斯蒙特变送器三种常见故障
  7. 基于NanoPi M4B的自动浇水系统
  8. 分页插件 limit使用失败
  9. 【简搜题】一个墙裂推荐的考试搜题网站
  10. 第一次使用爱斯维尔(Elsevier)论文模板注意事项