直方图计算
目标
本文档尝试解答如下问题:
如何使用OpenCV函数split将图像分割成单通道数组。
如何使用OpenCV函数calcHist计算图像阵列的直方图。
如何使用OpenCV函数normalize归一化数组。
阿拉斯加北极熊Note
在上一篇中(直方图均衡化)我们介绍了一种特殊直方图叫做图像直方图。现在我们从更加广义的角度来考虑直方图的概念,继续往下读!
什么是直方图?
直方图是对数据的集合统计,并将统计结果分布于一系列预定义的bins中。这里的数据不仅仅指的是灰度值(如上一篇您所看到的),统计数据可能是任何能有效描述图像的特征。先看一个例子吧。假设有一
个矩阵包含一张图像的信
息(灰度值):
如果我们按照某种方式去统计这些数字,会发生什么情况呢?既然已知数字的范围包含256个值,我们可以将这个范围分割成子区域(称作bins),如:
然后再统计掉入每一个的像素数目。采用这一方法来统计上面的数字矩
阵,我们可以得到下图(x轴表示bin,y轴表示各个bin中的像素个数)。
以上只是一个说明直方图如何工作以及它的用处的简单示例。直方图可以统计的不仅仅是颜色灰度,它可以统计任何图像特征(如梯度,方向等等)。
让我们再来搞清楚直方图的一些具体细节:
dims:需要统计的特征的数目,在上例中,dims=1因为我们仅仅统计了灰度值(灰度图像)。
bins:每个特征空间子区段的数目,在上例中,bins=16
range:每个特征空间的取值范围,在上例中,range=[0,255]
怎样去统计两个特征呢?在这种情况下,直方图就是3维的了,x轴和y轴分别
代表一个特征,z轴是掉入组合中的样本数目。同样的方法适用于更高维的情形(当然会变得很复杂)。
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=fal;
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));
///将直方图归一化到范围[ws]
normalize(r_hist,r_hist,ws,NORM_MINMAX,-1,Mat()); normalize(g_hist,g_hist,ws,NORM_MINMAX,-1,Mat()); normalize(b_hist,b_hist,ws,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);
return0;
}
解释
创建一些矩阵:
Mat src,dst;
装载原图像
src=imread(argv[1],1);
if(!src.data)
{return-1;}
使用OpenCV函数split将图像分割成3个单通道图像:
助人为乐的图片vector<Mat>rgb_planes;split(src,rgb_planes);
输入的是要被分割的图像(这里包含3个通道),输出的则是Mat类型的的向量。现在对每个通道配置直方图设置,既然我们用到了R,G和B通道,我们知
道像素值的范围是
设定bins数目():
int histSize=255;
设定像素值范围(前面已经提到,在0到255之间)
///设定取值范围(R,G,B))float range[]={0,255};const float*histRange= {range};
我们要把bin范围设定成同样大小(均一)以及开始统计前先清除直方图中的痕迹: bool uniform=true;bool accumulate=fal;
最后创建储存直方图的矩阵:
Mat r_hist,g_hist,b_hist;
下面使用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大小相同,清楚直方图痕迹
创建显示直方图的画布:
//创建直方图画布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));
在画直方图之前,先使用normalize归一化直方图,这样直方图bin中的值就被缩放到指定范围:
///将直方图归一化到范围[ws]normalize(r_hist,r_hist,0, ws,NORM_MINMAX,-1,Mat());normalize(g_hist,g_hist,0, ws,NORM_MINMAX,-1,Mat());normalize(b_hist,b_hist,0, ws,NORM_MINMAX,-1,Mat());
该函数接受下列参数:
r_hist:输入数组
r_hist:归一化后的输出数组(支持原地计算)
0及ws:这里,它们是归一化r_hist之后的取值极限
NORM_MINMAX:归一化方法(例中指定的方法将数值缩放到以上指定范围)
-1:指示归一化后的输出数组与输入数组同类型
Mat():可选的掩码
请注意这里如何读取直方图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))),