目录

1. 创建直方图 cv::calcHist()

2. 基本直方图操作

2.1. 直方图归一化

2.2. 直方图二值化

2.3. 找出最显著的区间

2.4 比较两个直方图

2.4.1 相关性方法 cv::COMP_CORRL

2.4.2 卡方方法 cv::COMP_CHISQR_ALT

2.4.3 交集法 cv::COMP_INTERSECT

2.4.4 巴氏距离 cv::COMP_BHATTACHARYYA


Opencv中可以使用cv::Mat或vector<>或稀疏矩阵表示直方图。

1. 创建直方图 cv::calcHist()

函数cv::calcHist()从一个或多个数组中创建直方图,直方图的维度和输入数组的维度或大小无关,取决于输入数组的数量。直方图中的每个维度对应于某个输入数组上的某个通道。

函数声明:

void cv::calcHist(
	const cv::Mat* images,  // C-style of images ,8U or 32F
	int nimages,  // number of images in 'images' array
	const int* channels,  // C-style list of int's,lists channels
	cv::InputArray mask,  // in 'images' count,iff mask nonzero
	cv::OutputArray hist,  // output histogram array
	int dims,  // hist dimensionality < cv::MAX_DIMS(32)
	const int* histSize,  // C-style array,hist sizes in each dim
	const float** ranges,  // C-style array,'dims' pairs set bin sizes
	bool uniform = true,  // true for uniform binning
	bool accumulate = false  // true,add to 'hist' else replace
);
void cv::calcHist(
	const cv::Mat* images,  // C-style of images ,8U or 32F
	int nimages,  // number of images in 'images' array
	const int* channels,  // C-style list of int's,lists channels
	cv::InputArray mask,  // in 'images' count,iff mask nonzero
	cv::SparseMat hist,  // output histogram array
	int dims,  // hist dimensionality < cv::MAX_DIMS(32)
	const int* histSize,  // C-style array,hist sizes in each dim
	const float** ranges,  // C-style array,'dims' pairs set bin sizes
	bool uniform = true,  // true for uniform binning
	bool accumulate = false  // true,add to 'hist' else replace
);

函数参数:

@images:输入数据,直方图从该参数中计算,images中的通道数可以不同,但它们的大小必须是相同的,数据类型必须统一,8位整数或32位浮点数。
@nimages:如果是C风格的images,nimages指定images中包含的数组个数;

@channels:指定直方图计算时,哪些通道需要被考虑,通道按顺序进行序列编号。images[0]的前N个通道被编号为0~N,接下来的images[1]中的M个通道被编号为N~N+M。channels中的元素个数是计算得出的直方图的维度。

@mask:可选掩膜,用来选择images中每个数组中哪些像素参数直方图计算。mask必须是8bit数组和images中的输入数组大小相同。如果不适用,传入cv::noArray().

@hist:直方图计算的输出值;

@dims:输出直方图的维度,该值是channels中元素的个数,每个维度的来源有channels指定。

@histSize:表示直方图中每个维度需要分为多少个区间,histSize中元素的个数应该等于dims。如 histSize=255表示每个维度分为255个区间。

@ranges:代表每个区间对应的值,C风格时,ranges中元素的个数必须等于dims。ranges[i]表示第i维中区间的结构,具体解释依赖uniform。STL风格,将平摊成一维数组。

@uniform:如果uniform=true,那么第i维中所有区间都等长,此时需要指定[最矮区间的下界,最高区间的上界)

@accumulate:指示在从images得到的数据被累加进hist之前,不要将其中的元素伤处、重新分配或设置为0.

计算直方图的主要目的置一是查看某个像素值在所有像素中所占的比例,可以用每个像素值的数组占一幅图像中所有像素的数目的比例来表示某个像素值数目的多少。另一种常用的归一化方式是寻找统计结果中最大数值,把所有结果除以这个最大的数值,以实现将所有数据都缩放到0~1之间。

2. 基本直方图操作

2.1. 直方图归一化

OpenCv提供normalize(0函数实现多种形式的归一化功能,函数声明:

void cv::normalize(
	cv::InputArray src,
	cv::InputOutputArray dst,
	double alpha = 1,
	double beta = 0,
	int notm_type = cv::NORM_L2,
	int dtype  =-1,
	cv::InputArray mark = cv::noArray()
);

参数说明:

@src:输入;

@dst:输出,数组的大小和原数组一致;

@alpha:用来规范值或者规范范围,是下限;

@beta:用来规范范围并且是上限,只在NORM_MINMAX中起作用;

@norm_type:归一化选择的数学公式类型;

@dtype:为负时,输出大小深度通道数都等于输入,为正时,输出只在深度与输入不同,不同的地方由dtype决定;

@mask:掩码,感兴趣区域。

cv::normalize分为范围归一化与数据值归一化。数值归一化是指将数值归一到[0,1]区间上,范围归一化指将数值归一化到[a,b]上,a,b为任意值。

归一化选择的数学公式类型,设数组中元素为{A1,A2,A3...An}

NORM_L1:

opencvsharp绘制图像bgr直方图 opencv画直方图_归一化

NORM_INF:

opencvsharp绘制图像bgr直方图 opencv画直方图_opencv_02

NORM_L2:

opencvsharp绘制图像bgr直方图 opencv画直方图_直方图_03

NORM_MINMAX,Ak不属于{max(Ai),min(Ai)},当Ak等于max(Ai)时,p=1,等于min(Ai)时,p=0:

opencvsharp绘制图像bgr直方图 opencv画直方图_计算机视觉_04

几种norm_type的区别:

1. NORM_L1/NORM_INF/NORM_L2模式下归一化结果与beta无关,只与alpha有关;

2. NORM_MINMAX中alpha和beta都起作用,需要注意的是alpha和beta的取值顺序与归一化结果无关,即alpha=255,beta=0和alpha=0,beta=255最后的归一化结果是相同的。

2.2. 直方图二值化

直方图二值化可以使用cv::threshold()函数实现。

2.3. 找出最显著的区间

有时希望找出有最多元素的区间,这种情况多发生在使用直方图表示概率分布的时候,可以使用cv::minMaxLoc()函数。该函数可以获取最大值、最小值以及它们所在的位置。

如果只是想找出一个n维非稀疏数组中的最大值或是最小值,可以使用cv::minMacIdx()。

2.4 比较两个直方图

通过某些特殊的判据对两个直方图的相似度进行度量,由函数cv::compareHist()实现。

可以通过对两幅图像取直方图,并通过给出的直方图比较方法来比较原来的两幅图像;
也可以借助直方图比较方法再图像中识别物体,方法是将物体的直方图和图片中不同的子区域的直方图进行比较,通过直方图相配的程度来判断该子区域是否有物体。

函数声明:

double cv::compareHist(
	cv::InputArray H1,  // first histogram to be compared
	cv::InputArray H2,  // second histogram to be compared
	int method  // comparison method 
);
double cv::compareHist(
	const cv::SparseMat& H1,  // first histogram to be compared
	const cv::SparseMat& H2,  // second histogram to be compared
	int method  // comparison method 
);

参数H1和H2是待比较的直方图,必须大小相同;参数method选择需要的距离度量。

method可取的方法:相关性方法(cv::COMP_CORRL)、卡方方法(cv::COMP_CHISQR_ALT)、交集法(cv::COMP_INTERSECT)、巴氏距离(cv::COMP_BHATTACHARYYA)。

2.4.1 相关性方法 cv::COMP_CORRL

相关性方法使用基于统计相关性的方法,实现了皮尔逊相关性系数方法,H1和H2解释为概率分布:

opencvsharp绘制图像bgr直方图 opencv画直方图_计算机视觉_05

opencvsharp绘制图像bgr直方图 opencv画直方图_计算机视觉_06

,N是直方图中区间的个数。

大相关性系数要比小相关性系数匹配的好。完全匹配的相关性系数为1,完全不匹配会得到-1,0对应着两个分布没有关系(随机组合)。

2.4.2 卡方方法 cv::COMP_CHISQR_ALT

距离度量,基于卡方检验统计量,是一种检验两个分布是否相关的替换检验方法。该种方法,低分值表示直方图匹配的好,完全匹配的分值为0,不完全匹配的分值无下限(依赖于直方图的大小)。

opencvsharp绘制图像bgr直方图 opencv画直方图_直方图_07

2.4.3 交集法 cv::COMP_INTERSECT

直方图交集法简单基于两个直方图的交集,该方法对两个直方图中的共同部分求和。

该度量方法中,高分值意味着匹配得比较好。如果两个直方图都进行了归一化,完全匹配对应的是1,完全不匹配对应的是0.

opencvsharp绘制图像bgr直方图 opencv画直方图_opencv_08

2.4.4 巴氏距离 cv::COMP_BHATTACHARYYA

巴氏距离匹配的效果越好,值越小,效果越差,值越大。完全匹配是0,完全无匹配是1

一般来说,在对直方图进行比较之前应该对其进行归一化,因为类似直方图交集这样的概念只有在归一化后才有意义。

根据《学习OpenCv3》的作者的经验,交集法在快速而粗略的匹配中效果很好,卡方方法和巴氏距离法效果更好,但速度更慢。

使用示例:

#include <opencv.hpp>
#include <iostream>

using namespace std;

int main(int argc,char *argv[])
{
	cv::Mat src = cv::imread(argv[1]);
	if(src.empty())
	{
		cout << "Cannot load " << argv[1] << endl;
		return -1;
	}

	cv::Mat hsv;
	cv::cvtColor(src,hsv,cv::COLOR_BGR2HSV);

	float h_ranges[] = {0,180};
	float s_ranges[] = {0,256};
	const float *ranges[] = {h_ranges,s_ranges};
	int histSize[] = {30,32};
	int ch[] = {0,1};

	cv::Mat hist;
	cv::calcHist(&hsv,1,ch,cv::noArray(),hist,2,histSize,ranges,true);
	cv::normalize(hist,hist,0,255,cv::NORM_MINMAX);

	int scale = 10;
	cv::Mat hist_img(histSize[0]*scale,histSize[1]*scale,CV_8UC3);

	// 绘制
	for(int h=0;h<histSize[0];h++)
	{
		for(int s=0;s<histSize[1];s++)
		{
			float hval = hist.at<float>(h,s);
			cv::rectangle(hist_img,
				cv::Rect(h*scale,s*scale,scale,scale),
				cv::Scalar::all(hval),-1);
		}
	}

	cv::imshow("src",src);
	cv::imshow("H-S histogram",hist_img);
	cv::waitKey(0);


	return 0;

}

总结直方图的基本操作步骤:

1. 读取图像;
2. 三通道图像拆分为单个单通道图像,此步骤可以省略,那么计算直方图的时候就要注意传入的参数;
3. 计算直方图;
4. 归一化或二值化或取显著区间;
5. 比较直方图;

参考:《学习OpenCv3》第13章