opencv c++函数 imgproc模块 16 直方图计算

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

目标

本文档尝试解答如下问题:

  • 如何使用OpenCV函数 split 将图像分割成单通道数组。
  • 如何使用OpenCV函数 calcHist 计算图像阵列的直方图。
  • 如何使用OpenCV函数 normalize 归一化数组。

Note

 

在上一篇中 (直方图均衡化) 我们介绍了一种特殊直方图叫做 图像直方图 。现在我们从更加广义的角度来考虑直方图的概念,继续往下读!

什么是直方图?

  • 直方图是对数据的集合 统计 ,并将统计结果分布于一系列预定义的 bins 中。

  • 这里的 数据 不仅仅指的是灰度值 (如上一篇您所看到的), 统计数据可能是任何能有效描述图像的特征。

  • 先看一个例子吧。 假设有一个矩阵包含一张图像的信息 (灰度值 0-255):

    ../../../../../_images/Histogram_Calculation_Theory_Hist0.jpg
  • 如果我们按照某种方式去 统计 这些数字,会发生什么情况呢? 既然已知数字的 范围 包含 256 个值, 我们可以将这个范围分割成子区域(称作 bins), 如:

    \begin{array}{l}[0, 255] = { [0, 15] \cup [16, 31] \cup ....\cup [240,255] } \\range = { bin_{1} \cup bin_{2} \cup ....\cup bin_{n = 15} }\end{array}

    然后再统计掉入每一个 bin_{i} 的像素数目。采用这一方法来统计上面的数字矩阵,我们可以得到下图( x轴表示 bin, y轴表示各个bin中的像素个数)。

    ../../../../../_images/Histogram_Calculation_Theory_Hist1.jpg
  • 以上只是一个说明直方图如何工作以及它的用处的简单示例。直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等)。

  • 让我们再来搞清楚直方图的一些具体细节:

    1. dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像)。
    2. bins: 每个特征空间 子区段 的数目,在上例中, bins = 16
    3. range: 每个特征空间的取值范围,在上例中, range = [0,255]
  • 怎样去统计两个特征呢? 在这种情况下, 直方图就是3维的了,x轴和y轴分别代表一个特征, z轴是掉入 (bin_{x}, bin_{y}) 组合中的样本数目。 同样的方法适用于更高维的情形 (当然会变得很复杂)。

OpenCV的直方图计算

OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist 。 支持高达 32 维的直方图。下面的代码演示了如何使用该函数计算直方图!

源码

  • 本程序做什么?

    • 装载一张图像
    • 使用函数 split 将载入的图像分割成 R, G, B 单通道图像
    • 调用函数 calcHist 计算各单通道图像的直方图
    • 在一个窗口叠加显示3张直方图
  • 下载代码: 点击 这里

  • 代码一瞥:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

/** @函数 main */
int main( int argc, char** argv )
{
  Mat src, dst;

 /// 装载图像
 src = imread( argv[1], 1 );

 if( !src.data )
   { return -1; }

 /// 分割成3个单通道图像 ( R, G 和 B )
 vector<Mat> rgb_planes;
 split( src, rgb_planes );

 /// 设定bin数目
 int histSize = 255;

 /// 设定取值范围 ( R,G,B) )
 float range[] = { 0, 255 } ;
 const float* histRange = { range };

 bool uniform = true; bool accumulate = false;

 Mat r_hist, g_hist, b_hist;

 /// 计算直方图:
 calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
 calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
 calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 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) );

 /// 将直方图归一化到范围 [ 0, histImage.rows ]
 normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
 normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
 normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );

 /// 在直方图画布上画出直方图
 for( int i = 1; i < histSize; i++ )
   {
     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  );
     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(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  );
    }

 /// 显示直方图
 namedWindow("calcHist Demo", CV_WINDOW_AUTOSIZE );
 imshow("calcHist Demo", histImage );

 waitKey(0);

 return 0;

}

解释

  1. 创建一些矩阵:

    Mat src, dst;
    
  2. 装载原图像

    src = imread( argv[1], 1 );
    
    if( !src.data )
      { return -1; }
    
  3. 使用OpenCV函数 split 将图像分割成3个单通道图像:

    vector<Mat> rgb_planes;
    split( src, rgb_planes );
    

    输入的是要被分割的图像 (这里包含3个通道), 输出的则是Mat类型的的向量。

  4. 现在对每个通道配置 直方图 设置, 既然我们用到了 R, G 和 B 通道, 我们知道像素值的范围是 [0,255]

    1. 设定bins数目 (5, 10...):

      int histSize = 255;
      
    2. 设定像素值范围 (前面已经提到,在 0 到 255之间 )

      /// 设定取值范围 ( R,G,B) )
      float range[] = { 0, 255 } ;
      const float* histRange = { range };
      
    3. 我们要把bin范围设定成同样大小(均一)以及开始统计前先清除直方图中的痕迹:

      bool uniform = true; bool accumulate = false;
      
    4. 最后创建储存直方图的矩阵:

      Mat r_hist, g_hist, b_hist;
      
    5. 下面使用OpenCV函数 calcHist 计算直方图:

      /// 计算直方图:
      calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
      calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
      calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
      

      参数说明如下:

      • &rgb_planes[0]: 输入数组(或数组集)
      • 1: 输入数组的个数 (这里我们使用了一个单通道图像,我们也可以输入数组集 )
      • 0: 需要统计的通道 (dim)索引 ,这里我们只是统计了灰度 (且每个数组都是单通道)所以只要写 0 就行了。
      • Mat(): 掩码( 0 表示忽略该像素), 如果未定义,则不使用掩码
      • r_hist: 储存直方图的矩阵
      • 1: 直方图维数
      • histSize: 每个维度的bin数目
      • histRange: 每个维度的取值范围
      • uniform 和 accumulate: bin大小相同,清楚直方图痕迹
  5. 创建显示直方图的画布:

    // 创建直方图画布
    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) );
    
  6. 在画直方图之前,先使用 normalize 归一化直方图,这样直方图bin中的值就被缩放到指定范围:

    /// 将直方图归一化到范围 [ 0, histImage.rows ]
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    

    该函数接受下列参数:

    • r_hist: 输入数组
    • r_hist: 归一化后的输出数组(支持原地计算)
    • 0 及 histImage.rows: 这里,它们是归一化 r_hist 之后的取值极限
    • NORM_MINMAX: 归一化方法 (例中指定的方法将数值缩放到以上指定范围)
    • -1: 指示归一化后的输出数组与输入数组同类型
    • Mat(): 可选的掩码
  7. 请注意这里如何读取直方图bin中的数据 (此处是一个1维直方图):

      /// 在直方图画布上画出直方图
      for( int i = 1; i < histSize; i++ )
        {
          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  );
    
          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(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  );
        }
    
    
    使用了以下表达式:
    
    .. code-block:: cpp
    
       r_hist.at<float>(i)
    
    
      :math:`i` 指示维度,假如我们要访问2维直方图,我们就要用到这样的表达式:
    
    .. code-block:: cpp
    
       r_hist.at<float>( i, j )
  8. 最后显示直方图并等待用户退出程序:

    namedWindow("calcHist Demo", CV_WINDOW_AUTOSIZE );
    imshow("calcHist Demo", histImage );
    
    waitKey(0);
    
    return 0;
    

结果

  1. 使用下图作为输入图像:

    ../../../../../_images/Histogram_Calculation_Original_Image.jpg
  2. 产生以下直方图:

    ../../../../../_images/Histogram_Calculation_Result.jpg
OpenCV入门(一)—— 使用cmake编译工程及基本模块介绍 OpenCV学习笔记 1.OpenCV入门 因为我是在学习slam的过程中遇到了OpenCV的问题,所以我就决定先把OpenCV的基本内容给摸清,并且学习的环境是在Ubuntu16.04下,所以我选择使用C++进行代码的编写,并且使用cmake对工程进行编译。 在我此前安装ROS系统的时候已经下载并安装了OpenCV库,所以可以直接上手,而无需再进行配置。如果有需要可以参考链接下载并安装Open... 阅读详情

相关推荐

OpenCV直方图均衡化与CLAHE算法详解

直方图均衡化是数字图像处理中的基础对比度增强技术,通过重新分配像素灰度值使输出直方图近似均匀分布。其核心是构建累积分布函数(CDF)映射,OpenCV采用查找表(LUT)优化实现高效计算。针对全局均衡化的局限性,CLAHE算法通过分块处理引入局部适应性,关键参数clipLimit控制对比度增强强度。这些技术在医学影像、遥感图像等领域有广泛应用,OpenCV的实现结合了双线性插值消除块效应,并进行了多线程等性能优化。理解直方图均衡化与CLAHE的原理有助于在图像增强、计算机视觉任务中合理选择参数,平衡对比度增

weixin_34290631的博客 362

C++ 超详细14Bit,16Bit,24Bit,32Bit图像直方图统计并绘制显示(详细图文教程_附C++代码)

C++ OpenCv提供了函数calcHist(),可以很方便的帮助我们统计一幅8Bit图像的直方图,但是对于10Bit,12Bit,14Bit,16Bit,24Bit,32Bit等高位图像就不适用了,无法统计,这时候就需要我们自己手写代码统计高位图像的直方图

qq_40280673的博客 6037

OpenCV4(C++)—— 直方图

直方图(Histogram)最开始在统计学中被提出,由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据类型,纵轴表示分布情况。在图像领域,直方图用来更直观地展现各像素值出现的频率,例如常用的灰度直方图反映的是一幅图像中各灰度像素值(0-255)出现的频率之间的关系。

qq_43199575的博客 2656

OpenCV 直方图统计函数 cv::calcHist算是彻底弄明白了

在代码中,计算两个通道,通过参数histSize指定,每个通道都分成8个分区,因此可能有64种组合结果,因此二维直方图的大小为8*8的矩阵。在图像中(3,10)这个组合的数据只有一个,对应的分区编号是(3,2),因此二维直方图中(3,2)位置的数据值为1,即上图中绿色标记的位置。在图像中 (3, 11)这个组合的数据有两个, (3, 11)对应的分区编号是(3,3),因此上图中的红色标记的位置的统计值为2。此时需要将dims参数设置为2。

lijing_lj928的专栏 3276

图像处理之直方图统计C++实现

1. 介绍 在分析图像、物体和视频信息的时候,我们经常用直方图来表达我们关注的信息。直方图计算机视觉中应用广泛。例如,通过判断帧与帧之间边缘和颜色的统计量是否出现巨大变化,来检测视频中场景的变换。 直方图只是简单地将数据归入预定义的组,并在每个组内进行计数。也可以选择对数据提取特征、再对特征进行计数,这里的特征可以是梯度的长度、梯度的方向、颜色或者其他任何可以反映数据特点的特征,也就是说,直方图是一种用来揭示数据分布的统计特性的工具。直方图的维度通常比原始数据的维度低。 OpenCV中使用数组来表示直方.

程序员深度学习 9158

C++ 超详细8Bit图像直方图统计并绘制显示,不使用calcHist()函数(详细图文教程)

C++ OpenCv中提供的函数calcHist()可以很方便的帮助我们统计一幅8Bit图像的直方图,但是有时候我们不想用OpenCv提供的函数统计,想自己根据自己的需求来做一个统计,那么看这个教程就对了,学会这种方法不仅是8Bit图像可以自己手写统计,10Bit,12Bit,14Bit,16Bit,24Bit,32Bit等图像,都可以自己手写代码实现直方图统计并显示出来。关于16Bit图像直方图统计并显示的具体教程,看我下一篇文章。

qq_40280673的博客 6294

opencv c++函数 imgproc模块 8 Sobel 导数

目标 本文档尝试解答如下问题: 如何使用OpenCV函数 Sobel 对图像求导。如何使用OpenCV函数 Scharr 更准确地计算  核的导数。 原理 Note   以下内容来自于Bradski和Kaehler的大作: Learning OpenCV . 上面两节我们已经学习了卷积操作。一个最重要的卷积运算就是导数的计算(或者近似计算)

CV菜鸟的学习之路 2037

OpenCV C++20 学习笔记】imgproc模块-绘图基础

介绍OpenCVimgproc模块中的一些基本的绘图方法,包括绘制线条、圆形、椭圆、多边形等。

TeamLee的博客 754

Eclipse - undefined reference to opencv_core opencv_imgproc opencv_highgui opencv_objdetect

Eclipse - undefined reference to opencv_core opencv_imgproc opencv_highgui opencv_objdetect

程永强 1447

基于C++OpenCV实现图像检索系统:从颜色直方图到工程实践

图像检索是计算机视觉领域的核心技术,其核心原理在于将图像内容转化为可计算的数学特征向量,并通过相似度度量进行高效比对。颜色直方图作为一种经典的特征表示方法,通过统计图像在HSV等色彩空间中的颜色分布,生成稳定的特征向量,具有计算高效、对几何变换鲁棒性强的技术价值。在工程实践中,结合巴氏距离等相似度度量算法,可以构建高效的检索系统,广泛应用于以图搜图、相册管理和内容去重等场景。本文聚焦于使用C++OpenCV从零实现一个基于颜色特征的图像检索引擎,深入探讨了特征提取、相似度计算与系统架构等关键环节,为理解更

weixin_34023982的博客 384

opencv c++函数 imgproc模块 9 Laplace 算

目标 本文档尝试解答如下问题: 如何使用OpenCV函数 Laplacian 实现 Laplacian 算 的离散模拟。 原理 前一节我们学习了 Sobel 算 ,其基础来自于一个事实,即在边缘部分,像素值出现”跳跃“或者较大的变化。如果在此边缘部分求取一阶导数,你会看到极值的出现。正如下图所示: 如果在边缘部分求二阶导数会出现什么情况?

CV菜鸟的学习之路 1746

重新认识OpenCVC++视角下的历史演进、功能特性以及OpenCV 4.11新特性

多年来,我深入探索并见证了OpenCV的成长,心中始终充满钦佩与敬意。尽管商业机器视觉算法在某些领域表现出色,但OpenCV绝非逊色。每当有人以轻蔑的口吻谈论它时,我都忍不住想为其正名——它不仅是开源社区的瑰宝,更是无数开发者实现梦想的基石。无论从功能的丰富性、性能的优化,还是社区的活跃度来看,OpenCV都值得被尊重与推崇。

懒人的技术笔记 2247

opencv各个模块功能总结

OpenCV计算机视觉开源库,由一系列C、C++类构成,包含了大量的C++、Python接口,主要的算法包括图像处理、机器视觉等。 做计算机视觉、SLAM等方向必须要掌握opencv的使用。 使用OpenCV已经好久一段时间了,主要用到的就那几个主要的模块,现在对OpenCV的框架个各模块的功能做一个介绍。 主要模块介绍: 1.【core】核心模块 OpenCV基本数据结...

baobiegui3066的博客 359

OpenCV核心模块解析与工程实践指南

计算机视觉作为人工智能的重要分支,其核心在于图像处理与特征提取。OpenCV作为开源计算机视觉库,通过模块化设计实现了从基础图像操作到高级视觉算法的完整覆盖。core模块提供Mat等基础数据结构,imgproc模块实现滤波、几何变换等处理,features2d模块支持特征检测与匹配。在工程实践中,合理使用SIMD指令集、并行计算和内存优化技术可显著提升性能,典型应用包括工业检测、SLAM和视频分析等场景。掌握OpenCV核心模块的设计原理与优化技巧,是开发高效视觉系统的关键。

weixin_33841503的博客 430

OpenCV-Python实战(1)——OpenCV简介与图像处理基础

OpenCV 是一个的跨平台计算机视觉库,可以运行在 Linux、Windows 和 Mac OS 操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时也提供了 Python 接口,实现了图像处理和计算机视觉方面的很多通用算法。在本文中,将介绍 OpenCV 库,包括它的主要模块和典型应用场景,同时使用 OpenCV-Python 实战讲解图像处理基础要点。

盼小辉丶的博客 27万+

OpenCV 概念、整体架构、各模块主要功能

在目录下,会发现上述文件夹中有个名为的 hpp 文件,里面存放的是OpenCV 中与新模块构造相关的说明代码,打开可以发现其定义的是 OpenCV 所有组件的宏,具体如下。/**/工程文件包含路径为下面就是 OpenCV 的所有模块, 按照宏定义的顺序依次介绍。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 857

VC++OpenCV实战:Windows桌面图像灰度直方图工具开发全流程

图像直方图是数字图像处理中的基础分析工具,它通过统计图像中各灰度级的像素数量分布,直观反映图像的亮度、对比度等关键特征。其原理在于将图像数据映射为统计图表,为图像增强、分割等高级操作提供量化依据。在工程实践中,掌握直方图计算与可视化技术,对于深入理解图像特性、进行算法调试与效果评估具有重要价值。特别是在Windows桌面应用开发场景中,结合VC++OpenCV库,可以实现从图像读取、灰度转换、直方图统计到图形界面集成的完整链路。本文以**灰度直方图**和**OpenCV配置**为核心,详细解析如何在Vis

weixin_30432579的博客 314

OpenCV】【YUV】OpenCV中YUV颜色空间变换到RGB颜色空间的转换公式

OpenCV中YUV颜色空间变换到RGB颜色空间采用的公式如下: 浮点: {R=1.164(Y−16)+1.596(V−128)G=1.164(Y−16)−0.813(V−128)−0.391(U−128)B=1.64(Y−16)+2.018(U−128)\footnotesize \begin{cases} R=1.164(Y-16)+1.596(V-128) \\ G=1.164(Y-16) ...

gy笨瓜的博客 3001

OpenCV_01 简介+无版权安装+模块分析

OpenCV是应用广泛的开源图像处理库,我们以其为基础,介绍相关的图像处理方法:包括基本的图像处理方法:几何变换,形态学变换,图像平滑,直方图操作,模板匹配,霍夫变换等;特征提取和描述方法:理解角点特征,Harris和Shi-Tomas算法,SIFT/SURF算法,Fast算法,ORB算法等;还有OpenCV在视频操作中的应用,最后的案例是使用OpenCV进行人脸检测。 1.1 图像是什么 图像是人类视觉的基础,是自然景物的客观反映,是人类认识世界和人类本身的重要源泉。“图”是物体反射或透射光的分.

小李的研究生学习日记 5580
上一篇: opencv c++函数 imgproc模块 15 直方图均衡化
下一篇: opencv c++函数 imgproc模块 17 直方图对比
淡淡的生活
博客等级 码龄16年 341粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值