1、calcHist函数
函数作用:
calcHist函数来计算图像直方图
2、calcHist函数调用形式
C++: 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 accumulate=false )
参数详解:
onst Mat* images:输入图像
int nimages:输入图像的个数
const int* channels:需要统计直方图的第几通道
InputArray mask:掩膜,,计算掩膜内的直方图 ...Mat()
OutputArray hist:输出的直方图数组
int dims:需要统计直方图通道的个数
const int* histSize:指的是直方图分成多少个区间,,,就是 bin的个数
const float** ranges: 统计像素值得区间
bool uniform=true::是否对得到的直方图数组进行归一化处理
bool accumulate=false:在多个图像时,是否累计计算像素值得个数
3、opencv代码
解释
-
创建一些矩阵:
-
装载原图像
-
使用OpenCV函数 split 将图像分割成3个单通道图像:
输入的是要被分割的图像 (这里包含3个通道), 输出的则是Mat类型的的向量。
-
现在对每个通道配置 直方图 设置, 既然我们用到了 R, G 和 B 通道, 我们知道像素值的范围是
-
设定bins数目 (5, 10...):
-
设定像素值范围 (前面已经提到,在 0 到 255之间 )
-
我们要把bin范围设定成同样大小(均一)以及开始统计前先清除直方图中的痕迹:
-
最后创建储存直方图的矩阵:
-
下面使用OpenCV函数 calcHist 计算直方图:
参数说明如下:
- &rgb_planes[0]: 输入数组(或数组集)
- 1: 输入数组的个数 (这里我们使用了一个单通道图像,我们也可以输入数组集 )
- 0: 需要统计的通道 (dim)索引 ,这里我们只是统计了灰度 (且每个数组都是单通道)所以只要写 0 就行了。
- Mat(): 掩码( 0 表示忽略该像素), 如果未定义,则不使用掩码
- r_hist: 储存直方图的矩阵
- 1: 直方图维数
- histSize: 每个维度的bin数目
- histRange: 每个维度的取值范围
- uniform 和 accumulate: bin大小相同,清楚直方图痕迹
-
-
创建显示直方图的画布:
-
在画直方图之前,先使用 normalize 归一化直方图,这样直方图bin中的值就被缩放到指定范围:
该函数接受下列参数:
- r_hist: 输入数组
- r_hist: 归一化后的输出数组(支持原地计算)
- 0 及 histImage.rows: 这里,它们是归一化 r_hist 之后的取值极限
- NORM_MINMAX: 归一化方法 (例中指定的方法将数值缩放到以上指定范围)
- -1: 指示归一化后的输出数组与输入数组同类型
- Mat(): 可选的掩码
-
请注意这里如何读取直方图bin中的数据 (此处是一个1维直方图):
-
最后显示直方图并等待用户退出程序:
多通道场景:
- Mat src, hsv;
- if(!(src=imread("d:/picture/lena.bmp")).data)
- return -1;
- cvtColor(src, hsv, CV_BGR2HSV);
- vector<Mat> hsv_plane;
- split(hsv, hsv_plane);
- Mat inputs[]={hsv_plane[1], hsv_plane[2], hsv_plane[0]};
- vector<Mat> mixmat_plane;
- mixmat_plane.push_back(hsv_plane[2]);
- mixmat_plane.push_back(hsv_plane[0]);
- Mat mixmat;
- merge(mixmat_plane, mixmat);
- Mat mixed[]={mixmat,hsv_plane[1]};
- int vbins = 128, sbins = 128, hbins = 128;
- int histSize[] = {sbins, vbins, hbins};
- float sranges[] = { 0, 256};
- float vranges[] = { 0, 256};
- float hranges[] = { 0, 256};
- const float*ranges[] = {sranges, vranges, hranges};
- MatND hist;
- //#define SINGLE_MAT
- #define MIX_MAT
- #ifdef SINGLE_MAT
- /*
- use one multi-channel mat, channels param gives the channels used;
- 使用多通道的图像计算多维直方图,可以计算1,2,3维的;
- */ int channels[] = {1, 2};
- calcHist(&hsv, 1, channels, Mat(),hist, 2, histSize, ranges,true, false );
- #elif defined MIX_MAT
- /*
- use mix mat array, the first elem is a single channel mat, second is a two channel mat;
- 使用混合通道图像数组,第1个图像是2通道的,第2个是单通道的;
- channels指定每一维对应的通道;
- */
- int channels[] = {1, 2, 0};
- // #define DIM_2
- #ifdef DIM_2
- //统计二维直方图;
- calcHist(mixed, 2, channels, Mat(),hist, 2, histSize, ranges,true, false);
- #else
- //统计三维直方图;
- calcHist(mixed, 2, channels, Mat(),hist, 3, histSize, ranges,true, false);
- #endif
- #else
- /*
- use multi-mat arrays, channels param gives the array mat and its channels used;
- 使用都是单通道图像数组计算2维直方图--也可以计算3维的;
- */
- int channels[] = {2, 1};
- hbins = 1;
- calcHist(inputs, 3, channels, Mat(),hist, 2, histSize, ranges,true, false );
- #endif
- #ifndef MIX_MAT
- double maxVal=0;
- minMaxLoc(hist, 0, 0, 0, 0);//only can process mat that dims<=2--minMaxLoc只能处理2维以下的;
- #endif
- int scale = 4;
- Mat histImg = Mat::zeros(vbins*scale, sbins*scale, CV_8UC3);
- float *hist_sta = new float[sbins];
- float *hist_val = new float[vbins];
- float *hist_hue = new float[hbins];
- memset(hist_val, 0, vbins*sizeof(float));
- memset(hist_sta, 0, sbins*sizeof(float));
- memset(hist_hue, 0, hbins*sizeof(float));
- for( int s = 0; s < sbins; s++ )
- {
- for( int v = 0; v < vbins; v++ )
- {
- for(int h=0; h<hbins; h++)
- {
- #ifdef MIX_MAT
- //-----------------------------------------------------------//
- #ifdef DIM_2
- float binVal = hist.at<float>(s, v);
- #else
- float binVal = hist.at<float>(s, v, h);
- hist_hue[h] += binVal;
- #endif
- //-----------------------------------------------------------//
- #else
- float binVal = hist.at<float>(s, v);
- int intensity = cvRound(binVal*255/maxVal);
- rectangle( histImg, Point(s*scale, v*scale),Point((s+1)*scale-1, (v+1)*scale-1), Scalar::all(intensity), CV_FILLED);
- #endif
- hist_val[v] += binVal;
- hist_sta[s] += binVal;
- }
- }
- }
- //find max bin value;
- double max_sta=.0, max_val=.0,max_hue=.0;
- for(int i=0; i<sbins; ++i)
- {
- if(hist_sta[i]>max_sta)
- max_sta = hist_sta[i];
- }
- for(int i=0; i<vbins; ++i)
- {
- if(hist_val[i]>max_val)
- max_val = hist_val[i];
- }
- for(int i=0; i<hbins; ++i)
- {
- if(hist_hue[i]>max_hue)
- max_hue = hist_hue[i];
- }
- Mat sta_img = Mat::zeros(310, sbins*scale+20, CV_8UC3);
- Mat val_img = Mat::zeros(310, vbins*scale+20, CV_8UC3);
- Mat hue_img = Mat::zeros(310, hbins*scale+20, CV_8UC3);
- for(int i=0; i<sbins; ++i)
- {
- int intensity = cvRound(hist_sta[i]*(sta_img.rows-10)/max_sta);
- rectangle(sta_img, Point(i*scale+10, sta_img.rows-intensity),Point((i+1)*scale-1+10, sta_img.rows-1), Scalar(0,255,0), 1);
- }
- for(int i=0; i<vbins; ++i)
- {
- int intensity = cvRound(hist_val[i]*(val_img.rows-10)/max_val);
- rectangle(val_img, Point(i*scale+10, val_img.rows-intensity),Point((i+1)*scale-1+10, val_img.rows-1), Scalar(0,0,255), 1);
- }
- for(int i=0; i<hbins; ++i)
- {
- int intensity = cvRound(hist_hue[i]*(hue_img.rows-10)/max_hue);
- rectangle(hue_img, Point(i*scale+10, hue_img.rows-intensity),Point((i+1)*scale-1+10, hue_img.rows-1), Scalar(255,0,0), 1);
- }
- namedWindow( "Source");
- imshow( "Source", src );
- namedWindow( "Histogram");
- imshow( "Histogram", histImg );
- namedWindow("dim1");
- imshow("dim1", sta_img);
- namedWindow("dim2");
- imshow("dim2", val_img);
- namedWindow("dim3");
- imshow("dim3", hue_img);
Mat src, hsv;
if(!(src=imread("d:/picture/lena.bmp")).data)
return -1;
cvtColor(src, hsv, CV_BGR2HSV);
vector<Mat> hsv_plane;
split(hsv, hsv_plane);
Mat inputs[]={hsv_plane[1], hsv_plane[2], hsv_plane[0]};
vector<Mat> mixmat_plane;
mixmat_plane.push_back(hsv_plane[2]);
mixmat_plane.push_back(hsv_plane[0]);
Mat mixmat;
merge(mixmat_plane, mixmat);
Mat mixed[]={mixmat,hsv_plane[1]};
int vbins = 128, sbins = 128, hbins = 128;
int histSize[] = {sbins, vbins, hbins};
float sranges[] = { 0, 256};
float vranges[] = { 0, 256};
float hranges[] = { 0, 256};
const float*ranges[] = {sranges, vranges, hranges};
MatND hist;
//#define SINGLE_MAT
#define MIX_MAT
#ifdef SINGLE_MAT
/*
use one multi-channel mat, channels param gives the channels used;
使用多通道的图像计算多维直方图,可以计算1,2,3维的;
*/ int channels[] = {1, 2};
calcHist(&hsv, 1, channels, Mat(),hist, 2, histSize, ranges,true, false );
#elif defined MIX_MAT
/*
use mix mat array, the first elem is a single channel mat, second is a two channel mat;
使用混合通道图像数组,第1个图像是2通道的,第2个是单通道的;
channels指定每一维对应的通道;
*/
int channels[] = {1, 2, 0};
// #define DIM_2
#ifdef DIM_2
//统计二维直方图;
calcHist(mixed, 2, channels, Mat(),hist, 2, histSize, ranges,true, false);
#else
//统计三维直方图;
calcHist(mixed, 2, channels, Mat(),hist, 3, histSize, ranges,true, false);
#endif
#else
/*
use multi-mat arrays, channels param gives the array mat and its channels used;
使用都是单通道图像数组计算2维直方图--也可以计算3维的;
*/
int channels[] = {2, 1};
hbins = 1;
calcHist(inputs, 3, channels, Mat(),hist, 2, histSize, ranges,true, false );
#endif
#ifndef MIX_MAT
double maxVal=0;
minMaxLoc(hist, 0, 0, 0, 0);//only can process mat that dims<=2--minMaxLoc只能处理2维以下的;
#endif
int scale = 4;
Mat histImg = Mat::zeros(vbins*scale, sbins*scale, CV_8UC3);
float *hist_sta = new float[sbins];
float *hist_val = new float[vbins];
float *hist_hue = new float[hbins];
memset(hist_val, 0, vbins*sizeof(float));
memset(hist_sta, 0, sbins*sizeof(float));
memset(hist_hue, 0, hbins*sizeof(float));
for( int s = 0; s < sbins; s++ )
{
for( int v = 0; v < vbins; v++ )
{
for(int h=0; h<hbins; h++)
{
#ifdef MIX_MAT
//-----------------------------------------------------------//
#ifdef DIM_2
float binVal = hist.at<float>(s, v);
#else
float binVal = hist.at<float>(s, v, h);
hist_hue[h] += binVal;
#endif
//-----------------------------------------------------------//
#else
float binVal = hist.at<float>(s, v);
int intensity = cvRound(binVal*255/maxVal);
rectangle( histImg, Point(s*scale, v*scale),Point((s+1)*scale-1, (v+1)*scale-1), Scalar::all(intensity), CV_FILLED);
#endif
hist_val[v] += binVal;
hist_sta[s] += binVal;
}
}
}
//find max bin value;
double max_sta=.0, max_val=.0,max_hue=.0;
for(int i=0; i<sbins; ++i)
{
if(hist_sta[i]>max_sta)
max_sta = hist_sta[i];
}
for(int i=0; i<vbins; ++i)
{
if(hist_val[i]>max_val)
max_val = hist_val[i];
}
for(int i=0; i<hbins; ++i)
{
if(hist_hue[i]>max_hue)
max_hue = hist_hue[i];
}
Mat sta_img = Mat::zeros(310, sbins*scale+20, CV_8UC3);
Mat val_img = Mat::zeros(310, vbins*scale+20, CV_8UC3);
Mat hue_img = Mat::zeros(310, hbins*scale+20, CV_8UC3);
for(int i=0; i<sbins; ++i)
{
int intensity = cvRound(hist_sta[i]*(sta_img.rows-10)/max_sta);
rectangle(sta_img, Point(i*scale+10, sta_img.rows-intensity),Point((i+1)*scale-1+10, sta_img.rows-1), Scalar(0,255,0), 1);
}
for(int i=0; i<vbins; ++i)
{
int intensity = cvRound(hist_val[i]*(val_img.rows-10)/max_val);
rectangle(val_img, Point(i*scale+10, val_img.rows-intensity),Point((i+1)*scale-1+10, val_img.rows-1), Scalar(0,0,255), 1);
}
for(int i=0; i<hbins; ++i)
{
int intensity = cvRound(hist_hue[i]*(hue_img.rows-10)/max_hue);
rectangle(hue_img, Point(i*scale+10, hue_img.rows-intensity),Point((i+1)*scale-1+10, hue_img.rows-1), Scalar(255,0,0), 1);
}
namedWindow( "Source");
imshow( "Source", src );
namedWindow( "Histogram");
imshow( "Histogram", histImg );
namedWindow("dim1");
imshow("dim1", sta_img);
namedWindow("dim2");
imshow("dim2", val_img);
namedWindow("dim3");
imshow("dim3", hue_img);
- </pre><pre name="code" class="cpp">
- <pre name="code" class="cpp">#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 = false;
- 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) );
- /// 将直方图归一化到范围 [ 0, histImage.rows ]
- normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
- normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
- normalize(b_hist, b_hist, 0, histImage.rows, 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);
- return 0;
- }
</pre><pre name="code" class="cpp">
<pre name="code" class="cpp">#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 = false;
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) );
/// 将直方图归一化到范围 [ 0, histImage.rows ]
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
normalize(b_hist, b_hist, 0, histImage.rows, 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);
return 0;
}