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 );
}

 

五、直方图的比较

有了几个直方图,我们就可以比较直方图之间的差别。

要比较直方图,我们首先要设定比较的尺度或者方法,一般有四种:

  1. Correlation ( CV_COMP_CORREL )  范围从0到1。1为完全相关,0为完全不相关。

    d(H_1,H_2) =  \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}

    where

    \bar{H_k} =  \frac{1}{N} \sum _J H_k(J)

    and N is the total number of histogram bins.

  2. Chi-Square ( CV_COMP_CHISQR )  范围从0到正无穷。0为完全相似,数字越大,直方图差别越大。(数字幅度变化较大)

    d(H_1,H_2) =  \sum _I  \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}

  3. Intersection ( method=CV_COMP_INTERSECT )  范围从0到正无穷,数字越大,表示越相似,因为直方图交集越多。数字越小,表示差别越大。(数字幅度变化较小)

    d(H_1,H_2) =  \sum _I  \min (H_1(I), H_2(I))

  4. Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )  范围从0到1。0表示完全相似。1表示完全不同。

    d(H_1,H_2) =  \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}

一般在比较直方图之前,要先对直方图归一化处理,使用上一步所说的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 直方图的范围

 

 

posted @ 2015-01-02 17:06  nipan  阅读(581)  评论(0编辑  收藏  举报