直方图匹配、对比:calcHist ,minMaxLoc,compareHist
- 1、calcHist()函数
- 2、归一化:normalize()函数
- 3、minMaxLoc()函数
- 4、compareHist()函数
- 5、示例一:H-S彩色图像的色调、饱和度二维直方图绘制
- 6、示例二:灰度值一维直方图绘制
- 7、示例三:三灰度值一维直方图绘制
- 8、示例四:直方图相识度对比
参考博客:
灰度直方图(一维直方图)calcHist()函数、minMaxLoc()函数、normalize()函数
直方图(Histogram)又称柱状图、质量分布图,是一种统计报告图。直方图由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据类型,纵轴表示分布情况。在图像处理上,直方图是图像信息统计的有力工具
1、calcHist()函数
用于计算一个或者多个这列的直方图
void calcHist(
const Mat* images, // 输入的数组或数组集 CV_8U 或CV_32F
int nimages, // 输入数组的个数 (几幅图像)
const int* channels, // 需要统计直方图的第几通道(dim)
InputArray mask, // 掩膜,计算掩膜内的直方图
OutputArray hist, // 输出的直方图数组,一个二维数组
int dims, // 需要统计直方图通道的个数(需要统计的特征的数目)
const int* histSize, // bin的个数,指的是直方图分成多少个区间
const float** ranges, // 每一维数值的取值范围,统计像素值得区间如:[0,255]
bool uniform=true, // 是否对得到的直方图数组进行归一化处理
bool accumulate=false // 在多个图像时,是否累计计算像素值得个数
)
2、归一化:normalize()函数
查找数组中的全局最小值和最大值。
void normalize(
InputArray src, // 输入数组(图像)
OutputArray dst, // 输出数组(图像),与输入图像类型尺寸一样
double alpha=1, // 表示range normalization模式的最小值。
double beta=0, // 表示range normalization模式的最大值,不用于norm normalization(范数归一化)模式。
int norm_type=NORM_L2, // 表示归一化的类型,可以有以下的取值:// NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。// NORM_INF:此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)。// NORM_L1:归一化数组的L1-范数(绝对值的和)。// NORM_L2:归一化数组的(欧几里德)L2-范数。
int dtype=-1, // dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype)。
InputArray mask=noArray() // 操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。
);
3、minMaxLoc()函数
寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置。
minMaxLoc函数的其他应用:
寻找一幅图像的匹配的模板,可以在一段视频里寻找出我们感兴趣的东西,比如条形码的识别就可能需要这样类似的一个工作提取出条形码区域(当然这样的方法并不鲁棒)。而OpenCV已经为我们集成好了相关的功能。函数为matchTemplate。
所谓模板匹配就是在一幅图像中寻找和模板图像(patch)最相似的区域。该函数的功能为,在输入源图像Source image(I)中滑动框,寻找各个位置与模板图像Template image(T)的相似度,并将结果保存在结果矩阵result matrix(R)中。该矩阵的每一个点的亮度表示与模板T的匹配程度。然后可以通过函数minMaxLoc定位矩阵R中的最大值(该函数也可以确定最小值)。
void cv::minMaxLoc(
InputArray src, // 输入单通道阵列
double * minVal, // 指向返回的最小值的指针;如果不需要,则使用NULL。
double * maxVal = 0, // 指向返回的最大值的指针;如果不需要,则使用NULL。
Point * minLoc = 0, // 指向返回的最小位置的指针(二维情况下);如果不需要,则使用NULL。
Point * maxLoc = 0, // 指向返回的最大位置的指针(二维情况下);如果不需要,则使用NULL。
InputArray mask = noArray() // 用于选择子数组的可选掩码。
)
4、compareHist()函数
对输入的两张图像进行直方图均衡化及直方图计算步骤后,可以对两个图像的直方图进行对比,并通过对比的结果得到一些我们想要的结论。
直方图比较应用
1)图像相似度比较
如果我们有两张图像,并且这两张图像的直方图一样,或者有极高的相似度,那么在一定程度上,我们可以认为这两幅图是一样的,这就是直方图比较的应用之一。
2)分析图像之间关系
两张图像的直方图反映了该图像像素的分布情况,可以利用图像的直方图,来分析两张图像的关系。
// 原型一
double compareHist(
InputArray H1, // 第一个直方图
InputArray H2, // 第二,比较与H1大小相同的直方图(直方图尺寸相同,图片可以不同)
int method //
) // 原型二
double compareHist(
const SparseMat &H1,
const SparseMat &H2,
int method
)
4种衡量直方图相似度的对比标准:(H1表示第一直方图,H2第二直方图)
OpenCv提供了5种对比直方图的方式:CORREL(相关)、CHISQR(卡方)、INTERSECT(相交)、BHATTACHARYYA、EMD(最小工作距离),其中CHISQR速度最快,EMD速度最慢且有诸多限制,但是EMD的效果最好
1).相关系数的标准,Correlation(method=CV_COMP_CORREL)值越大,相关度越高,最大值为1,最小值为0;且N等于直方图中bin的个数。
2).卡方系数的标准(method=CV_COMP_CHISQR)值越小,相关度越高,最大值无上界,最小值0;
c.相交系数的标准(method=CV_COMP_INTERSECT)值越大,相关度越高,最大值为9.455319,最小值为0;
d.巴氏系数(Bhattacharyya)的标准(method=CV_COMP_BHATTACHARYYA)值越小,相关度越高,最大值为1,最小值为0
5、示例一:H-S彩色图像的色调、饱和度二维直方图绘制
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main()
{//1、载入源图,转化为HSV颜色空间(2通道,故统计两中特征两:色调,饱和度)Mat srcImage, hsvImage;srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }cvtColor(srcImage,hsvImage, CV_BGR2HSV);// 2、参数准备//将色调量化为30个等级,将饱和度量化为32个等级int hueBinNum = 30; // 色调的直方图直条数量int saturationBinNum = 32; // 饱和度的直方图直条数量int histSize[ ] = {hueBinNum, saturationBinNum};// 定义色调的变化范围为0到179// 定义饱和度的变化范围为0(黑、白、灰)到255(纯光谱颜色)float hueRanges[] = { 0, 180 };float saturationRanges[] = { 0, 256 };const float* ranges[] = { hueRanges, saturationRanges };MatND dstHist;//参数准备,calcHist函数中将计算第0通道和第1通道的直方图int channels[] = {0, 1};// 3、正式调用calcHist,进行直方图计算calcHist( &hsvImage, // 输入的数组1, // 数组个数为1channels, // 通道索引 0, 1 通道Mat(), // 不使用掩膜dstHist, // 输出的目标直方图2, // 需要计算的直方图的维度为2histSize, // 存放每个维度的直方图尺寸的数组ranges, // 每一维数值的取值范围数组true, // 指示直方图是否均匀的标识符,true表示均匀的直方图false ); // 累计标识符,false表示直方图在配置阶段会被清零// 4、为绘制直方图准备参数double maxValue=0;//最大值minMaxLoc(dstHist, 0, &maxValue, 0, 0);//查找数组和子数组的全局最小值和最大值存入maxValue中int scale = 10;Mat histImg = Mat::zeros(saturationBinNum*scale, hueBinNum*10, CV_8UC3);// 5、双层循环,进行直方图绘制for( int hue = 0; hue < hueBinNum; hue++ )for( int saturation = 0; saturation < saturationBinNum; saturation++ ){float binValue = dstHist.at<float>(hue, saturation); //直方图组距的值int intensity = cvRound(binValue*255/maxValue); //强度//正式进行绘制rectangle( histImg, Point(hue*scale, saturation*scale),Point( (hue+1)*scale - 1, (saturation+1)*scale - 1),Scalar::all(intensity),CV_FILLED );}// 6、显示效果图imshow( "素材图", srcImage );imshow( "H-S 直方图", histImg );waitKey(0);return 0;
}
结果:
6、示例二:灰度值一维直方图绘制
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main(int argc, char *argv[])
{//1、载入源图,转化为灰度图(1通道,故只统计一种特征量:灰度值)Mat srcImage;srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }cvtColor(srcImage,srcImage, CV_BGR2GRAY);imshow("原图",srcImage);// 2、定义变量MatND dstHist; // 输出的二维目标直方图,二维数组int dims = 1; // 统计的特征数目(仅灰度值)float hranges[] = {0, 255}; // 每一维数值的取值范围const float *ranges[] = {hranges}; // 这里需要为const类型int size = 256; // 每个维度直方图尺寸int channels = 0;// 3、计算图像的直方图calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges); // cv 中是cvCalcHistint scale = 1;Mat dstImage(size * scale, size, CV_8U, Scalar(0));// 4、获取最大值和最小值double minValue = 0;double maxValue = 0;minMaxLoc(dstHist,&minValue, &maxValue, 0, 0); // 在cv中用的是cvGetMinMaxHistValue// 5、绘制出直方图int hpt = saturate_cast<int>(0.9 * size);for(int i = 0; i < 256; i++){float binValue = dstHist.at<float>(i); // 注意hist中是float类型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("一维直方图", dstImage);waitKey(0);return a.exec();
}
结果:
7、示例三:三灰度值一维直方图绘制
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 1、载入源图,转化为HSV颜色模型Mat srcImage;srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }imshow("原图",srcImage);// 2、参数准备int bins = 256;int hist_size[] = {bins};float range[] = { 0, 256 };const float* ranges[] = { range};MatND redHist,greenHist,blueHist;// 3、进行直方图的计算(红色分量部分)// 4、进行直方图的计算(绿色分量部分)// 5、进行直方图的计算(蓝色分量部分)int channels_r[] = {0};int channels_g[] = {1};int channels_b[] = {2};calcHist( &srcImage, 1, channels_r, Mat(), redHist, 1, hist_size, ranges,true,false );calcHist( &srcImage, 1, channels_g, Mat(), greenHist, 1, hist_size, ranges, true,false );calcHist( &srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges,true,false );//-----------------------绘制出三色直方图------------------------
// // 1、参数准备
// double maxValue_red,maxValue_green,maxValue_blue;
// minMaxLoc(redHist, 0, &maxValue_red, 0, 0);
// minMaxLoc(grayHist, 0, &maxValue_green, 0, 0);
// minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0);
// int scale = 1;
// int histHeight=256;
// Mat histImage = Mat::zeros(histHeight,bins*3, CV_8UC3); // 创建直方图背景图// // 2、正式开始绘制
// for(int i=0;i<bins;i++)
// {
// // 2.1 参数准备
// float binValue_red = redHist.at<float>(i);
// float binValue_green = grayHist.at<float>(i);
// float binValue_blue = blueHist.at<float>(i);
// int intensity_red = cvRound(binValue_red*histHeight/maxValue_red); //要绘制的高度
// int intensity_green = cvRound(binValue_green*histHeight/maxValue_green); //要绘制的高度
// int intensity_blue = cvRound(binValue_blue*histHeight/maxValue_blue); //要绘制的高度// // 2.2 绘制红色分量的直方图
// rectangle(histImage,Point(i*scale,histHeight-1),
// Point((i+1)*scale - 1, histHeight - intensity_red),
// CV_RGB(255,0,0));// // 2.3绘制绿色分量的直方图
// rectangle(histImage,Point((i+bins)*scale,histHeight-1),
// Point((i+bins+1)*scale - 1, histHeight - intensity_green),
// CV_RGB(0,255,0));// // 2.4绘制蓝色分量的直方图
// rectangle(histImage,Point((i+bins*2)*scale,histHeight-1),
// Point((i+bins*2+1)*scale - 1, histHeight - intensity_blue),
// CV_RGB(0,0,255));// }
// // 3、在窗口中显示出绘制好的直方图
// imshow( "图像的RGB直方图", histImage );//-----------------------绘制出三色折线图------------------------int histWidth = 800; // 直方图 宽int histHeight = 512; // 直方图 高int binWidth = cvRound((double)histWidth/bins); // 每直条(组距)宽,取整Mat histImage(histHeight,histWidth,CV_8UC3,Scalar(0,0,0));//归一化处理normalize()函数的参数解析见后面normalize(redHist,redHist,0,histImage.rows,NORM_MINMAX,-1,Mat());normalize(greenHist,greenHist,0,histImage.rows,NORM_MINMAX,-1,Mat());normalize(blueHist,blueHist,0,histImage.rows,NORM_MINMAX,-1,Mat());//画直方图for( int i = 1; i < bins; i++ ){line( histImage, Point( binWidth*(i-1), histHeight - cvRound(redHist.at<float>(i-1)) ) ,Point( binWidth*(i), histHeight - cvRound(redHist.at<float>(i)) ),Scalar( 0, 0, 255), 2, 8, 0 );line( histImage, Point( binWidth*(i-1), histHeight - cvRound(greenHist.at<float>(i-1)) ) ,Point( binWidth*(i), histHeight - cvRound(greenHist.at<float>(i)) ),Scalar( 0, 255, 0), 2, 8, 0 );line( histImage, Point( binWidth*(i-1), histHeight - cvRound(blueHist.at<float>(i-1)) ) ,Point( binWidth*(i), histHeight - cvRound(blueHist.at<float>(i)) ),Scalar( 255, 0, 0), 2, 8, 0 );}// 显示直方图imshow("图像的RGB折线图", histImage );waitKey(0);return a.exec();
}
结果:
8、示例四:直方图相识度对比
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;string convertToString(double d)
{ostringstream os;if (os<<d){return os.str();}return "invalid conversion";
}int main()
{// 1、声明储存基准图像和另外两张对比图像的矩阵( RGB 和 HSV )Mat srcImage_base, hsvImage_base;Mat srcImage_test1, hsvImage_test1;Mat srcImage_test2, hsvImage_test2;Mat hsvImage_halfDown;// 2、载入基准图像(srcImage_base) 和两张测试图像srcImage_test1、srcImage_test2,并显示srcImage_base = imread( "F:/C++/2. OPENCV 3.1.0/TEST/q.jpg",1 );srcImage_test1 = imread( "F:/C++/2. OPENCV 3.1.0/TEST/w.jpg", 1 );srcImage_test2 = imread( "F:/C++/2. OPENCV 3.1.0/TEST/e.jpg", 1 );// 3、将图像由BGR色彩空间转换到 HSV色彩空间cvtColor( srcImage_base, hsvImage_base, CV_BGR2HSV );cvtColor( srcImage_test1, hsvImage_test1, CV_BGR2HSV );cvtColor( srcImage_test2, hsvImage_test2, CV_BGR2HSV );// 4、创建包含基准图像下半部的半身图像(HSV格式)hsvImage_halfDown = hsvImage_base( Range( hsvImage_base.rows/2, hsvImage_base.rows - 1 ), Range( 0, hsvImage_base.cols - 1 ) );// 5、初始化计算直方图需要的实参// 对hue通道使用30个bin,对saturatoin通道使用32个binint h_bins = 30; int s_bins = 60;int histSize[] = { h_bins, s_bins };// hue的取值范围从0到256, saturation取值范围从0到180float h_ranges[] = { 0, 256 };float s_ranges[] = { 0, 180 };const float* ranges[] = { h_ranges, s_ranges };// 使用第0和第1通道int channels[] = { 0, 1 };// 6、创建储存直方图的 MatND 类的实例:MatND baseHist;MatND halfDownHist;MatND testHist1;MatND testHist2;//7、计算基准图像,两张测试图像,半身基准图像的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(), testHist1, 2, histSize, ranges, true, false );normalize( testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat() );calcHist( &hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false );normalize( testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat() );// 8、按顺序使用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, testHist1, compare_method );double base_test2 = compareHist( baseHist, testHist2, compare_method );//给每个图像上添加文字,内容为该图片和原始图片的比较结果int j=i+1;putText(srcImage_base, convertToString(base_base), Point(50, 50*j), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 255), 2, LINE_AA);putText(hsvImage_halfDown, convertToString(base_half), Point(50, 50*j), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 255), 2, LINE_AA);putText(srcImage_test1, convertToString(base_test1), Point(50, 50*j), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);putText(srcImage_test2, convertToString(base_test2), Point(50, 50*j), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 255), 2, LINE_AA);//输出结果printf( " 方法 [%d] 的匹配结果如下:\n\n 【基准图 - 基准图】:%f, 【基准图 - 半身图】:%f,\n【基准图 - 测试图1】:%f, 【基准图 - 测试图2】:%f \n-----------------------------------------------------------------\n", i, base_base, base_half , base_test1, base_test2 );}//显示载入的3张图像imshow("基准图像",srcImage_base);imshow("测试图像1",srcImage_test1);imshow("测试图像2",srcImage_test2);imshow("下半部的半身图像",hsvImage_halfDown);waitKey(0);return 0;
}
结果: