opencv笔记(二十一)——关于直方图的几个操作
一、直方图均衡化
考虑四种图像。
在暗色图像中,直方图的组成成分集中在灰度级低的一侧。
明亮图像的直方图倾向于灰度级高的一侧。
低对比度图像的直方图窄而集中于灰度级的中部。
高对比度图像的直方图的成分覆盖了灰度级很宽的范围。
在遇到前三种图像的时候,可能我们需要直方图均衡化,用来使一幅图像的像素占有全部可能的灰度级并且分布均匀,这样的图像有高对比度和多变的灰度色调。
OpenCV中,我们使用equalizeHist来对灰度图像进行直方图均衡化:
equalizeHist(InputArray src, OutputArray dst)
src一般为8位单通道的图像。
dst和src有相同的尺寸和类型。
二、如果一幅图像是三通道的,我们要对每个通道进行直方图的统计,那么该如何做呢?
分析一段OpenCV的实例代码我们便知:
// 使用split函数,将BGR三通道分开
vector<Mat> bgr_planes;
split(src, bgr_planes); // src is the source image we read into Mat before
// 使用calcHist函数进行直方图的统计,这个函数的使用关键在于它的参数的设置。它的参数设置比较复杂。
/// Establish the number of bins
int histSize = 256;/// Set the ranges ( for B,G,R) )
float range[] = { 0, 256 } ;
const float* histRange = { range };bool uniform = true;
bool accumulate = false;
Mat b_hist, g_hist, r_hist; // 这三个参数分别用来记录bgr的直方图
/// Compute the histograms:
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 );
calcHist的函数说明如下:
void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool acculate=false)
对照上边calcHist函数的实例,我们可以看出
image是待统计直方图的图像的输入,类型为指针,可以让我们给数组或者vector,支持多个image处理。我们这里只要处理一个,所以直接给地址。输入的图像如果有多个,则他们应该尺寸和深度都相同。
nimages说明输入的个数。
channels应该也是一个数组,我们这里只要处理一幅单通道的输入,应该给入int channels[] = { 0 },这里直接给0也行,不知道为什么。
mask是掩膜,如果mask非空,则mask需要跟输入有相同尺寸,mask为1的位置计入直方图。我们这里给Mat()。
hist是输出。
dims是直方图的维度。这里是1。就是一维直方图。还有三维直方图,返回三维的cv::Mat实例。
histSize是直方图的尺寸,这里是256.
histRange是每个维度的直方图的范围。
三、将直方图归一化:
接上例:
/// Normalize the result to [ 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的第一个参数是输入,第二个参数是输出,在第五个参数为NORM_MINMAX的情况下,第三个参数和第四个参数分别为下限和上限。第六个参数为负,说明输入和输出的类型相同。第七个参数为掩膜。
四、绘制直方图:
接上例:
int hist_w = 512; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize );
Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
/// Draw for each channel
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 );
}
五、直方图的比较
有了几个直方图,我们就可以比较直方图之间的差别。
要比较直方图,我们首先要设定比较的尺度或者方法,一般有四种:
Correlation ( CV_COMP_CORREL ) 范围从0到1。1为完全相关,0为完全不相关。
where
and is the total number of histogram bins.
Chi-Square ( CV_COMP_CHISQR ) 范围从0到正无穷。0为完全相似,数字越大,直方图差别越大。(数字幅度变化较大)
Intersection ( method=CV_COMP_INTERSECT ) 范围从0到正无穷,数字越大,表示越相似,因为直方图交集越多。数字越小,表示差别越大。(数字幅度变化较小)
Bhattacharyya distance ( CV_COMP_BHATTACHARYYA ) 范围从0到1。0表示完全相似。1表示完全不同。
一般在比较直方图之前,要先对直方图归一化处理,使用上一步所说的normalize函数,把上限改为1就好了。
在OpenCV的例子中,给了三幅图,是分别在不同的角度和光线下,对一只手掌拍摄的照片。然后比较这几张照片的HSV格式的H-S两维的直方图,忽略V。
对比直方图的方法是compareHist,是这么用的:
double compareHist(InputArray H1, InputArray H2, int method)
H1和H2是待对比的两幅直方图
method是对比的方法,从0到3分别是CV_COMP_CORREL, CV_COMP_CHESQR, CV_COMP_INTERSECT, CV_COMP_BHATTACHARYYA。
六、反投影直方图 Back Projection
反投影直方图简单说来,就是我们首先有了一份样本的直方图,然后拿到一幅图片,我们计算这幅图片上每个像素属于这个直方图的概率值。就叫反投影。
void calcBackProject(const Mat* images, int nimages, const int* channels, const SparseMat& hist, OutputArray backProject, const float** ranges, double scale=1, bool uniform=true)
image 待投影的图像,可以多个,但是要有一样的depth
nimages 图像个数
channels 用来投影的通道
hist 直方图(这个比较重要)
backProject 输出的图像
ranges 直方图的范围