Opencv学习之图像直方图
直方图(Histogram)是一种对数据分布情况的图形表示,是一种二维统计图表,它的两个坐标分别是统计样本和该样本对应的某个属性的度量。直方图广泛应用于很多计算机视觉运用当中,通过标记帧与帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变化。在每个兴趣点设置一个有相近特征的直方图所构成“标签”,用以确定图像中的兴趣点。边缘、色彩、角度等直方图构成了可以被传递给目标识别分类器的一个通用特征类型。色彩和边缘的直方图序列还可以用来识别网络视频是否被复制。
直方图就是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的bin(直条、组距)当中,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征,直方图获得的是数据分布的统计图,通常直方图的维数要低于原始数据。
图像直方图是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布。
直方图的意义如下:
*直方图是图像中像素强度分布的图形表达方式。
*它统计了每一个强度值所具有的像素个数。
直方图术语:
*dims:需要统计的特征的数目。
*bins:每个特征空间子区段的数目,又叫“直条”或“组距”。
*range:每个特征空间的取值范围。
计算直方图–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 accumulate=false)
*第一个参数,输入的数组(或数组集),它们需为相同的深度(CV_8U或CV_32F)和相同的尺寸。
*第二个参数,输入数组的个数,也就是第一个参数存放了多少张“图像”,有几个原数组。
*第三个参数,需要统计的通道(dim)索引。第一个数组通道从0到images[0].channels()-1,而第二个数组通道从images[0].channels()计算到images[0].channels()+images[1].channels()-1。
*第四个参数,可选的操作掩码。如果此掩码不为空,那么它必须为8位,并且与image[i]有同样大小的尺寸。这里的非零掩码元素用于标记出统计直方图的数组元素数据。
*第五个参数,输出的目标直方图,一个二维数组。
*第六个参数,需要计算的直方图的维度,必须是正数,且不大于CV_MAX_DIMS
*第七个参数,存放每个维度的直方图尺寸的数组。
*第八个参数,表示每一个维度数组(第六个参数dims)的每一维的边界阵列,可以理解为每一维数值的取值范围。
*第九个参数,指示直方图是否是均匀的标识符,默认值,true。
*第十个参数,累计标识符,默认值false,若为true,直方图在配置阶段不会被清零。此功能主要是允许从多个阵列中计算单个直方图,或者用于在特定的时间更新直方图。
找寻最值–minMaxLoc函数
函数的作用是在数组中找到全局最小值和最大值。
void minMaxLoc(inputArray src,double* minVal,double* maxVal=0,Point* minLoc=0,Point* maxLoc=0,inputArray mask=noArray())
*第一个参数,输入的单通道阵列。
*第二个参数,返回最小值的指针,若无须返回,此值置为NULL。
*第三个参数,返回的最大值的指针,若无须返回,此值置为NULL。
*第四个参数,返回最小位置的指针(二维情况下),若无须返回,此值置为NULL。
*第五个参数,返回最大位置的指针(二维情况下),若无须返回,此值置为NULL。
*第六个参数,用于选择子阵列的可选掩膜。
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//声明全局变量
Mat g_srcImage;
//声明全局函数
void on_1DHist(int ,void *);
void on_RGBHist(int ,void *);
//主函数
int main()
{
//载入源图像并显示
g_srcImage=imread("/Users/new/Desktop/2.jpg");
if(!g_srcImage.data){printf("读取源图像srcImage~!\n");return false;}
imshow("image[origin]",g_srcImage);
on_1DHist(0, 0);
on_RGBHist(0, 0);
waitKey(0);
return 0;
}
//定义回调函数
void on_1DHist(int ,void *)
{
//定义变量
MatND dstHist;
int dims=1;
float hranges[]= {0,255};
const float *ranges[]={hranges};
int size=256;
int channels=0;
//计算图像的直方图
Mat srcImage;
g_srcImage.copyTo(srcImage);
calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges);
int scale=1;
Mat dstImage(size*scale,size,CV_8U,Scalar(0));
//获取最大值和最小值
double minValue=0;
double maxValue=0;
minMaxLoc(dstHist, &minValue,&maxValue,0,0);
//绘制出直方图
int hpt=saturate_cast<int>(0.9*size);
for(int i=0;i<256;++i)
{
float binValue=dstHist.at<float>(i);
int realValue=saturate_cast<int>(binValue*hpt/maxValue);
rectangle(dstImage, Point(i*scale,size-1), Point((i+1)*scale-1,size-realValue), Scalar(255));
}
imshow("image[1Dhistogram]",dstImage);
}
void on_RGBHist(int ,void *)
{
//参数准备
int bins=256;
int hist_size[]={bins};
float range[]={0,256};
const float* ranges[]={range};
MatND redHist,grayHist,blueHist;
int channels_r[]={0};
Mat srcImage;
g_srcImage.copyTo(srcImage);
//进行直方图的计算(红色)
calcHist(&srcImage, 1, channels_r, Mat(), redHist, 1, hist_size, ranges ,true,false);
//进行直方图的计算(绿色)
int channels_g[]={1};
calcHist(&srcImage, 1, channels_g, Mat(), grayHist, 1, hist_size, ranges ,true,false);
//进行直方图的计算(蓝色)
int channels_b[]={2};
calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges ,true,false);
//绘制三色直方图
//参数准备
double maxValue_red,maxValue_gray,maxValue_blue;
minMaxLoc(redHist, 0,&maxValue_red,0,0);
minMaxLoc(grayHist, 0,&maxValue_gray,0,0);
minMaxLoc(blueHist, 0,&maxValue_blue,0,0);
int scale=1;
int histHeight=256;
Mat histImage=Mat::zeros(histHeight,bins*3,CV_8UC3);
//正式开始绘制
for(int i=0;i<bins;++i)
{
//参数准备
float binValue_red=redHist.at<float>(i);
float binValue_gray=grayHist.at<float>(i);
float binValue_blue=blueHist.at<float>(i);
int intensity_red=cvRound(binValue_red*histHeight/maxValue_red);//要绘制的高度
int intensity_gray=cvRound(binValue_gray*histHeight/maxValue_gray);//要绘制的高度
int intensity_blue=cvRound(binValue_blue*histHeight/maxValue_blue);//要绘制的高度
//绘制红色分量的直方图
rectangle(histImage, Point(i*scale,histHeight-1), Point((i+1)*scale-1,histHeight-intensity_red), Scalar(0,0,255));
//绘制绿色分量的直方图
rectangle(histImage, Point((i+bins)*scale,histHeight-1), Point((i+bins+1)*scale-1,histHeight-intensity_gray), Scalar(0,255,0));
//绘制蓝色分量的直方图
rectangle(histImage, Point((i+bins*2)*scale,histHeight-1), Point((i+bins*2+1)*scale-1,histHeight-intensity_blue), Scalar(255,0,0));
}
imshow("image[rgb]",histImage);
}
直方图对比–compareHist函数
函数用于对两幅直方图进行比较。
double compareHist(inputArray H1,inputArray H2,int method)
double compareHist(const sparseMat &H1,const sparseMat& H2,int method)
前两个参数是要比较的大小相同的直方图,第三个变量是所选择的距离标准。
(1)相关-correlation(method=CV_COMP_CORREL)
(2)卡方-Chi-Square(method=CV_COMP-CHISQR)
(3)直方图相交-intersection(method=CV_COMP_INTERSECT)
(4)Bhattacharyya距离(method=CV_COMP_BHATTACHARYYA/CV_COMP_HELLINGER)
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
//主函数
int main()
{
//声明存储基准图像和另外两张对比图像的矩阵(RGB和HSV)
Mat srcImage_base,hsvImage_base;
Mat srcImage_test1,hsvImage_test1;
Mat srcImage_test2,hsvImage_test2;
Mat hsvImage_halfDown;
//载入基准图像和两张测试图像并显示
srcImage_base=imread("/Users/new/Desktop/4.jpg");
srcImage_test1=imread("/Users/new/Desktop/6.jpg");
srcImage_test2=imread("/Users/new/Desktop/7.jpg");
imshow("image[base]",srcImage_base);
imshow("image[test1]",srcImage_test1);
imshow("image[test2]",srcImage_test2);
//将图像由RGB色彩空间转换到HSV色彩空间
cvtColor(srcImage_base, hsvImage_base, COLOR_BGR2HSV);
cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
cvtColor(srcImage_test2, hsvImage_test2, COLOR_BGR2HSV);
//创建包含基准图像下半部的半身图像(HSV格式)
hsvImage_halfDown=hsvImage_base(Range(hsvImage_base.rows/2,hsvImage_base.rows-1),Range(hsvImage_base.cols/2,hsvImage_base.cols-1));
//初始化计算直方图需要的实参
//对H色调通道使用30个bin,对S饱和度使用32个bin
int h_bins=50;int s_bins=60;
int histSize[]={h_bins,s_bins};
//H色调取值范围从0~256,S饱和度取值范围从0~180
float h_ranges[]={0,256};
float s_ranges[]={0,180};
const float* ranges[]={h_ranges,s_ranges};
//使用第0和第1通道
int channels[]={0,1};
//创建存储直方图的MatND实例
MatND baseHist,halfDownHist,test1Hist,test2Hist;
//计算基准图像,两张测试图像,半身基准图像的HSV直方图
calcHist(&hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges,true,false);
//归一化
normalize(baseHist,baseHist,0,1,NORM_MINMAX,-1,Mat());
calcHist(&hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges,true,false);
normalize(halfDownHist,halfDownHist,0,1,NORM_MINMAX,-1,Mat());
calcHist(&hsvImage_test1, 1, channels, Mat(), test1Hist, 2, histSize, ranges,true,false);
normalize(test1Hist,test1Hist,0,1,NORM_MINMAX,-1,Mat());
calcHist(&hsvImage_test2, 1, channels, Mat(), test2Hist, 2, histSize, ranges,true,false);
normalize(test2Hist,test2Hist,0,1,NORM_MINMAX,-1,Mat());
//按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行比较
for(int i=0;i<4;++i)
{
int compare_method=i;
double base_base=compareHist(baseHist, baseHist, compare_method);
double base_half=compareHist(baseHist, halfDownHist, compare_method);
double base_test1=compareHist(baseHist, test1Hist, compare_method);
double base_test2=compareHist(baseHist, test2Hist, compare_method);
printf("方法[%d]的匹配结果如下:\n\n【基准图-基准图】:%f,【基准图-半身图】:%f,【基准图-测试图1】:%f,【基准图-测试图2】:%f\n---------------------------------------------------------------------------------------------\n",i,base_base,base_half,base_test1,base_test2);
}
printf("检测结束。\n");
waitKey(0);
return 0;
}
基准图像:
测试图像1:
测试图像2:
测试结果:
方法0-相关和方法2-直方图相交都是表示值越大相似度越高。
Opencv技巧
(1)定义直方图:MatND disHist;
(2)不使用掩膜:Mat();