科普丨【计算机视觉】OpenCV中直方图处理函数简述

作者Jason Ding
GitCafe博客主页(http://jasonding1354.gitcafe.io/)

计算直方图calcHist

直方图是对数据集合的统计 ,并将统计结果分布于一系列预定义的bins中。这里的数据不仅仅指的是灰度值 ,统计数据可能是任何能有效描述图像的特征。
假设有一个矩阵包含一张图像的信息 (灰度值 0-255):

既然已知数字的范围包含256个值, 我们可以将这个范围分割成子区域(称作 bins),如:

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

直方图可以统计的不仅仅是颜色灰度,它可以统计任何图像特征(如梯度,方向等等)。

直方图具体细节

dims: 需要统计的特征的数目,在上例中,dims = 1因为我们仅仅统计了灰度值(灰度图像)
bins: 每个特征空间子区段的数目,在上例中,bins = 16
range: 每个特征空间的取值范围,在上例中,range = [0,255]

OpenCV的直方图计算

OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数calcHist。支持高达32维的直方图。


1

2

3

4

5

6

7

8

9

10

11


void calcHist(

const Mat* arrays, // 图像源数组,同样深度(CV_8U or CV_32F),同样大小

int narrays, // 图片个数

const int* channels, // 通道

InputArray mask, // 掩码图像

OutputArray hist, // 返回的直方图

int dims, // 直方图的维数

const int* histSize, // 每一维上直方图的个数

const float** ranges, // 像素值的范围

bool uniform=true,

bool accumulate=false );

说明:

channels - 用来计算直方图的channels的数组
mask - 掩码。如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小相同,值为1的点将用来计算
dim - 直方图的维数。必须为正,并且不大于CV_MAX_DIMS(当前的OpenCV版本中为32,即最大可以统计32维的直方图)
histSize - 在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数
ranges - 用来进行统计的范围

反投影直方图

反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式。
简单的讲,所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。


1

2

3

4

5

6

7

8


void calcBackProject(

const Mat* arrays,

int narrays,

const int* channels,

InputArray hist,

OutputArray backProject,

const float** ranges,

double scale=1, bool uniform=true );

hist - 输入直方图
backProject - 反投影向量,这是一个单通道的向量,和arrays[0]具有相同的大小和深度

下面使用肤色直方图为例来解释反向投影的工作原理:
使用模型直方图(代表手掌的皮肤色调)来检测测试图像中的皮肤区域,

  • 对测试图像中的每个像素 ( p(i,j) ),获取色调数据并找到该色调( h(i,j), s(i,j) )在直方图中的bin的位置
  • 查询 模型直方图 中对应的bin( h(i,j), s(i,j) )并读取该bin的数值
  • 将此数值储存在新的图像中(BackProjection)。 你也可以先归一化 模型直方图 ,这样测试图像的输出就可以在屏幕显示了
  • 通过对测试图像中的每个像素采用以上步骤, 得到 BackProjection 结果图
  • 使用统计学的语言, BackProjection 中储存的数值代表了测试图像中该像素属于皮肤区域的概率。比如以上图为例, 亮起的区域是皮肤区域的概率更大(事实确实如此),而更暗的区域则表示更低的概率

阈值化

阈值是最简单的图像分割的方法。
应用举例:从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体)。这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割。
为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。(注意:阈值的选取依赖于具体的问题。即:物体在不同的图像中有可能会有不同的灰度值。)
一旦找到了需要分割的物体的像素点,我们可以对这些像素点设定一些特定的值来表示。(例如:可以将该物体的像素点的灰度值设定为:‘0’(黑色),其他的像素点的灰度值为:‘255’(白色);当然像素点的灰度值可以任意,但最好设定的两种颜色对比度较强,方便观察结果)。

阈值类型


阈值API


1

2

3

4

5

6


double threshold(

InputArray src,

OutputArray dst,

double thresh,

double maxVal,

int thresholdType);

均值漂移(Mean Shift)算法函数

该函数利用了迭代物体搜索算法,它要以一个物体的反射直方图(back projection)和初始位置作为输入。
搜索窗口的重心向反射直方图的质心(mass center)移动,该过程不断的重复,直到达到了迭代的次数(criteria.maxCount),或者窗口中心小于一个阈值(criteria.epsilon)。


1

2

3

4

5


int meanShift(

InputArray probImage, // Back projection of the object histogram

Rect& window, // Initial search window

TermCriteria criteria // Stop criteria for the iterative search algorithm.

);

Camshift算法函数

该函数首先利用meanShift()函数找到物体的中心,然后调整窗口的大小并找到最优的旋转角度。该函数返回一个rotated rectangle数据结构(包含物体的位置,大小和旋转角度)。下一次搜索窗口的位置可以通过RotatedRect::boundingRect()得到。


1

2

3

4

5


RotatedRect CamShift(

InputArray probImage, // Back projection of the object histogram

Rect& window, // Initial search window

TermCriteria criteria // Stop criteria for the underlying meanShift()

);

TermCriteria模板类

该类是作为迭代算法的终止条件使用的,其构造函数需要三个参数:一个是类型,第二个参数为迭代的最大次数,最后一个是特定的阈值。


1


TermCriteria(int type, int maxCount, double epsilon);

类型有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,分别代表着迭代终止条件为达到最大迭代次数终止,迭代到阈值终止,或者两者都作为迭代终止条件。

参考资料

机器学习技法课程,林轩田,台湾大学

转载请注明作者Jason Ding及其出处
GitCafe博客主页(http://jasonding1354.gitcafe.io/)

本文来源于"中国人工智能学会",原文发表时间"2016-11-01 "

时间: 2025-01-20 10:33:43

科普丨【计算机视觉】OpenCV中直方图处理函数简述的相关文章

opencv中的kmeans函数

问题描述 opencv中的kmeans函数 LBPimage是一个3通道的灰度图像.通过kmeans函数之后,分的两类,labels的各个分量都是0,只有第一个和最后一个元素是1.请问大神们,错在哪里. Mat LBPimage=LBP(imgs); int nl = LBPimage.rows; int nc = LBPimage.cols; Mat points; points.create(nc*nl, 1, CV_32FC1); int n = 0; for (int i = 0; i

opencv 中的inRange函数怎么用,求高人指点///,急求,大神给帮忙解决一下,谢谢

问题描述 opencv 中的inRange函数怎么用,求高人指点///,急求,大神给帮忙解决一下,谢谢 inRange函数的参数是什么意思,有没有详解??opencv 中的inRange函数怎么用,求高人指点///,急求,大神给帮忙解决一下,谢谢 解决方案 void cvInRangeS( const CvArr* src CvScalar lower CvScalar upper CvArr* dst );src 第一个原数组 lower 包括进的下边界. upper 不包括进的上边界 dst

jpg-opencv中的imread函数问题

问题描述 opencv中的imread函数问题 为什么使用imread函数时候,第二个参数的设置会最终影响是否能读入图片?比如jpg的用1的时候可以,用0的时候就说内存泄漏?但是用0不应该是强制转换成灰度图像?这真的必须按规定值使用吗? 解决方案 额,用人家的东西,得按照别人的规则使用啊 解决方案二: imread的第二个参数就是选择读出来的图是灰度图或者是RGB图的,如果程序运行中出现崩溃,可能是配置的库的问题,比如debug和release的库混着配置了,可以贴下你读图的代码和项目的附加依赖

matlab-MATLAB中的直方图均衡函数histeq在opencv中实现碰到的问题

问题描述 MATLAB中的直方图均衡函数histeq在opencv中实现碰到的问题 有没有人知道 MATALB中的直方图均衡函数histeq和opencv中的cvEqualizeHist()函数的区别,还有我把网上所以的直方图均衡函数都试了一遍 结果发现在opencv中均衡化后的图像的平均值 要大于MATLAB中均衡化后的平均值 有没有大神碰到过类似的情况

c++-我调用opencv中的函数计算出的hu矩没有旋转、缩放不变性,请大家帮我看下程序哪出问题了。

问题描述 我调用opencv中的函数计算出的hu矩没有旋转.缩放不变性,请大家帮我看下程序哪出问题了. #include #include #include using namespace std; using namespace cv; int main(int argc, char *argv[]) {//读入图片预处理 Mat image=imread("F:vs2010 project21.jpg"); //image.create(480, 640, CV_8UC1); na

目标跟踪学习笔记_1(opencv中meanshift和camshift例子的应用)

在这一节中,主要讲目标跟踪的一个重要的算法Camshift,因为它是连续自使用的meanShift,所以这2个函数opencv中都有,且都很重要.为了让大家先达到一个感性认识.这节主要是看懂和运行opencv中给的sample并稍加修改.      Camshift函数的原型为:RotatedRect CamShift(InputArray probImage, Rect& window, TermCriteria criteria).      其中probImage为输入图像直方图的反向投影

python opencv 显示直方图

04-python opencv显示直方图 04-python opencv显示直方图 概述 实现过程 引用与打开图片 灰度图像直方图 分离彩色图像的三通道并显示各通道直方图 在一幅图中绘制彩色图像的灰度直方图 等待键盘输入并关闭所有窗口 源代码 运行结果 参考 概述 本节实现的是提取出灰度图像和彩色图像的直方图. 显示灰度图像的灰度直方图 显示彩色图像各个通道的灰度直方图 在一幅图上显示三个通道的灰度直方图 实现过程 引用与打开图片 不再赘述,代码如下. import cv2 import n

计算方差-请教大神在opencv中怎么计算图像局部方差

问题描述 请教大神在opencv中怎么计算图像局部方差 请教大神在opencv中怎么计算图像局部方差, 请教大神在opencv中怎么计算图像局部方差, 请教大神在opencv中怎么计算图像局部方差,谢谢 解决方案 你也在学opencv吗,可以交流下 解决方案二: 局部,是正常的矩形还是不规则区域 矩形: 坐标的x y即像素点的x y根据长宽遍历 不规则: 我暂时没有计算过 你图像是灰阶还是RGB 如果灰阶 按照灰阶度使用公式即可,如果是RGB建议先用cvmerxx 函数忘记了 把RGB通道合成之

opencv-为何OpenCV中cvWaitKey(1)延时不是1ms?

问题描述 为何OpenCV中cvWaitKey(1)延时不是1ms? 我写了一个三帧差分法进行运动目标检测的程序,通过 start = GetTickCount(); if(cvWaitKey(1)>=0) break; end = GetTickCount(); printf("Diff_time = %ld, n",end-start); 来获取cvWaitKey(1)的运行时间,发现Diff_time为16,也就是cvWaitKey(1)耗时16ms,这条语句不是应该延时1