基于直方图的图像匹配方案(opencv3)

2018/10/20

1.最近一段时间可以说有点忙吧,一直在学习opencv3的基础内容,渐渐的也发现了很多好玩的事物,作为计算机开源视觉库,资源是及其丰富和充满奥秘的,记得和朋友说过,单纯调用一个库就像夹娃娃一样(并不是每一次都可以调用成功是吧,但是成功率或许会比真正的夹娃娃要高出不少),opencv看了也将近两个月,基础的知识也慢慢梳理了,会渐渐的发一些笔记吧!!!

2.直方图的简单介绍(一维)

2-1:直方图的简单介绍:

简单来说,直方图就是对数据统计的一种方法,描述的是对于像素强度的分布形式,通过自设的强度,来统计基于该强度值上的像素分布情况

2-2:直方图(一维)

下面为RGB三通道直方图的简单介绍

2-2-1:代码部分:

//实现三色直方图的绘制
//picture adress C:\\Users\\ASUS\\Pictures\\opencv_Pirture
//直方图 统计了每一个强度所拥有的像素个数
//通过给定的范围区间 实现不同的统计方式 

#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/core.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat src;
	src = imread("C:\\Users\\ASUS\\Pictures\\opencv_Pirture\\1.jpg");
	int bins = 600;
	int hist_size[] = { bins };//代表着全局范围 框的尺寸
	float range[] = {0, 256 };//确定像素的范围 在这里是为了实现对0--255像素区间强度的描述 
	const float* ranges[] = {range};
	MatND hist_r, hist_g, hist_b;//专门用来储存直方图
	int channels_r[] = { 0 };//channel_red
	calcHist(&src, 1, channels_r, Mat(), // do not use mask  
		hist_r, 1, hist_size, ranges,
		true, // the histogram is uniform  
		false);
	/*
	FUNCTION DETAILS:
	1.输入的数组集,需要有相同的深度
	2.输入数组的个数(前面有几张图)
	3.通道索引 数组名
	4.操作掩码
	5.输出目标(redhist) MatND
	6.直方图的维度(在这里为1维)
	7.直方图的尺寸 数组名
	8.每一维数据的范围 数组名
	9.直方图是否均匀
	10.累计标识符 (默认为false)
*/

	int channels_g[] = { 1 };//channel_green
	calcHist(&src, 1, channels_g, Mat(), // do not use mask  
		hist_g, 1, hist_size, ranges,
		true, // the histogram is uniform  
		false);

	int channels_b[] = { 2 };//channel_blue
	calcHist(&src, 1, channels_b, Mat(), // do not use mask  
		hist_b, 1, hist_size, ranges,
		true, // the histogram is uniform  
		false);
	double max_val_r, max_val_g, max_val_b;
	minMaxLoc(hist_r, 0, &max_val_r, 0, 0);
	minMaxLoc(hist_g, 0, &max_val_g, 0, 0);
	minMaxLoc(hist_b, 0, &max_val_b, 0, 0);
	/*FUNCTION DETAILS:
	1.输入的单通道阵列
	2.返回最小值指针
	3.返回最大值指针
	4.返回最小位置指针
	5.返回最大位置指针
	*/
	int scale = 1;
	int hist_height = 256;
	Mat hist_img = Mat::zeros(hist_height, bins * 3, CV_8UC3);
	for (int i = 0; i<bins; i++)
	{
		float bin_val_r = hist_r.at<float>(i);
		float bin_val_g = hist_g.at<float>(i);
		float bin_val_b = hist_b.at<float>(i);
		int intensity_r = cvRound(bin_val_r*hist_height / max_val_r);  //要绘制的高度  
		int intensity_g = cvRound(bin_val_g*hist_height / max_val_g);  //要绘制的高度  
		int intensity_b = cvRound(bin_val_b*hist_height / max_val_b);  //要绘制的高度  
		rectangle(hist_img, Point(i*scale, hist_height - 1),
			Point((i + 1)*scale - 1, hist_height - intensity_r),
			CV_RGB(255, 0, 0));

		rectangle(hist_img, Point((i + bins)*scale, hist_height - 1),
			Point((i + bins + 1)*scale - 1, hist_height - intensity_g),
			CV_RGB(0, 255, 0));

		rectangle(hist_img, Point((i + bins * 2)*scale, hist_height - 1),
			Point((i + bins * 2 + 1)*scale - 1, hist_height - intensity_b),
			CV_RGB(0, 0, 255));
	}
	imshow("Source", src);
	imshow("RGB Histogram", hist_img);
	while (waitKey(0) != 27);
	return 0;
}

2-2-2:效果图

 

基于直方图的图像匹配方案(opencv3)

3.基于直方图的简单匹配方案

3-1:匹配方法

对于opencv3而言,比较常见的图像匹配方案是通过surf算法进行特征点匹配(不详细说明,有兴趣的大家可以自行百度),,但是说实话,比较难实现(虽然效果很好),在opencv3中提供了一个函数compareHist()用于直方图的匹配,通过对函数的返回值的对比,实现对图像是否匹配的判断

3-2:compareHist(srcImage,dstImage,compare_method)

srcImage:原图

dstImage:输出图

compare_method:这个值以int形式出现,在opencv3中有着较好的封装,一般来说有四种方法

0:Correlation(相关) 反馈的数值越大 说明越匹配 归一化后完全匹配为1 完全不匹配为-1 随机为0

1:Chi_Square(卡方)完全匹配为0 不完全匹配为任意值

2:Intersection(直方图相较)数值越高代表着匹配值越高,数值越低代表着匹配值越低

3.Bhattacharyya(距离) 数值越低越好,越高代表图像越不匹配,完全匹配为0 不匹配为1

 

3-3:设计思路:构建直方图并且归一化,最后比较compareHist()的返回值(使用原图和原图比较以及原图和效果图比较,通过一个逻辑判断是否是相同的图)

(注:逻辑阈值为自设 可以自行设计为百分数参数或者任意逻辑输出)

代码如下:(在这里使用了方法2)

//直方图匹配
//进入hsv空间进行匹配
//C:\\Users\\ASUS\\Pictures\\opencv_Pirture\\base.jpg

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
	//【0】改变console字体颜色
	system("color 2F");
	//【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("C:\\Users\\ASUS\\Pictures\\opencv_Pirture\\base.jpg", 1);//基准图
	srcImage_test1 = imread("C:\\Users\\ASUS\\Pictures\\opencv_Pirture\\text.jpg", 1);//测试图1																			  
	imshow("基准图像", srcImage_base);
	imshow("测试图像1", srcImage_test1);

	// 【3】将图像由BGR色彩空间转换到 HSV色彩空间
	cvtColor(srcImage_base, hsvImage_base, COLOR_BGR2HSV);
	cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
	//【4】创建包含基准图像下半部的半身图像(HSV格式)
	//【5】初始化计算直方图需要的实参
	// 对hue通道使用30个bin,对saturatoin通道使用32个bin
	int h_bins = 50; int s_bins = 60;
	int histSize[] = { h_bins, s_bins };
	// hue的取值范围从0到256, saturation取值范围从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 };

	// 【6】创建储存直方图的 MatND 类的实例:
	MatND baseHist;
	MatND testHist1;
	// 【7】计算基准图像,测试图像
	calcHist(&hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false);
	normalize(baseHist, baseHist, 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());
	int compare_method = 1;
	double base_base = compareHist(baseHist, baseHist, 2);
	double base_text1 = compareHist(baseHist, testHist1, 2);
	//printf("基准值为%lf\n", base_base);
	cout << "原图基准值为" << base_base << endl;
	//printf("匹配值为%lf\n", base_text1);
	cout << "比较值为" << base_text1 << endl;
	if (base_base == base_text1)
	{
		cout << "图像匹配成功" << endl;
	}
	else
	{
		cout << "图像匹配失败" << endl;
	}
	cout << "检测结束" << endl;
	waitKey(0);
	return 0;
}

效果图:

基于直方图的图像匹配方案(opencv3)


基于直方图的图像匹配方案(opencv3)

注:该内容基于浅墨大神的opencv3编程入门,学完了直方图部分,基于书籍谈一下自己的认识和看法,如果有纰漏,请大家及时提出,小白一枚,希望大家多多指教!!!