反向投影的作用是什么?
反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
具体过程就是:
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
反向投影的结果是什么?
反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。通过图像的反向投影矩阵,我们实际上把原图像简单化了,简单化的过程实际上就是提取出图像的某个特征。以后我们可以用这个特征来对比两幅图,如果两幅图的反向投影矩阵相似或相同,那么我们就可以判定这两幅图这个特征是相同的
看完后,重点是直方图对比的方法
1、直方图对比概述
对输入的两张图像进行直方图均衡化及直方图计算步骤后,可以对两个图像的直方图进行对比,计算相似度,并通过对比的结果得到一些我们想要的结论。
2、直方图对比的应用
(1)图像相似度比较
如果我们有两张图像,并且这两张图像的直方图一样,或者有极高的相似度,那么在一定程度上,我们可以认为这两幅图是一样的,这就是直方图比较的应用之一。
(2)分析图像之间关系
两张图像的直方图反映了该图像像素的分布情况,可以利用图像的直方图,来分析两张图像的关系。
3、直方图比较原理
要比较两个直方图(H1 和 H2),首先必须要选择一个衡量直方图相似度的对比标准,我们设为d(H1,H2),
4、直方图比较方法
(1)相关性比较(Correlation)
(2)Chi-Square(卡方比较)
(3)Intersection(十字交叉性)
(4)Bhattacharyya distance(巴氏距离)
在直方图相似度计算时,巴氏距离获得的效果最好,但计算是最为复杂的。巴氏距离的计算结果,其值完全匹配为1,完全不匹配则为0。
API介绍——compareHist
(1)步骤
a.先用cvtColor()把图像从RGB色彩空间转换到HSV色彩空间;
b.计算图像的直方图,然后归一化到[0~1]之间,用到函数 calcHist() 和 normalize() ;
c.使用上述的四种方法之一进行比较,用到函数compareHist()。
(2)API介绍
函数一共有三个参数,一个输入图像,一个输出图像,一个比较方法。比较方法的取值的情况为上面的四种方法,在OpenCV中,每个都有自己的名字:Correlation ( CV_COMP_CORREL );Chi-Square ( CV_COMP_CHISQR );Intersection ( CV_COMP_INTERSECT );Bhattacharyya 距离( CV_COMP_BHATTACHARYYA )。
里要说明下为什么要用H-S直方图来作对比,因为BGR直方图我们只能三个通道分别计算,计算后也没法合在一起,也就是不能把图片作为一个整体来比较,而H-S就可以将图片作一个整体对比,所以这里用H-S直方图来作对比
Opencv代码
#define INPUT_TITLE0 "input image src"
#define INPUT_TITLE1 "input image srctest1"
#define INPUT_TITLE2 "input image srctest2"
#define OUTPUT_TITLE "name"
#include<iostream>
#include<math.h>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
string convertToString(double d);
int main() {
// 加载图像
Mat src, srctest1, srctest2;
src = imread("2.jpg");
srctest1 = imread("5.jpg");
srctest2 = imread("3.jpg");
if (!src.data || !srctest1.data || !srctest2.data)
{
cout << "ERROR : could not load image.";
return -1;
}
imshow("【src 原图】", src);
imshow("【srctest1 原图】", srctest1);
imshow("【srctest2 原图】", srctest2);
//从RGB色彩空间转化为HSV色彩空间
cvtColor(src, src, CV_BGR2HSV);
cvtColor(srctest1, srctest1, CV_BGR2HSV);
cvtColor(srctest2, srctest2, CV_BGR2HSV);
//定义直方图计算所需要的各种参数
int h_bins = 50;
int s_bins = 60;
int histSize[] = { h_bins,s_bins };
float h_ranges[] = { 0,180 };
float s_ranges[] = { 0,256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0,1 };//使用第0和第1通道
//MatND 是 Mat的别名,方便区分经过直方图计算处理后和输入图像
MatND hist_src;
MatND hist_srctest1;
MatND hist_srctest2;
//计算直方图并归一化处理
calcHist(&src, 1, channels, Mat(), hist_src, 2, histSize, ranges, true, false);
normalize(hist_src, hist_src, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&srctest1, 1, channels, Mat(), hist_srctest1, 2, histSize, ranges, true, false);
normalize(hist_srctest1, hist_srctest1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&srctest2, 1, channels, Mat(), hist_srctest2, 2, histSize, ranges, true, false);
normalize(hist_srctest2, hist_srctest2, 0, 1, NORM_MINMAX, -1, Mat());
//直方图比较
double src_src = compareHist(hist_src, hist_src, CV_COMP_CORREL);
double src_srctest1 = compareHist(hist_src, hist_srctest1, CV_COMP_CORREL);
double src_srctest2 = compareHist(hist_src, hist_srctest2, CV_COMP_CORREL);
double srctest1_srctest2 = compareHist(hist_srctest1, hist_srctest2, CV_COMP_CORREL);
cout << "src compare with src correlation value : " << src_src << endl;
cout << "src compare with srctest1 correlation value : " << src_srctest1 << endl;
cout << "src compare with srctest2 correlation value : " << src_srctest2 << endl;
cout << "srctest1 compare with srctest2 correlation value : " << srctest1_srctest2 << endl;
//给每个图像上添加文字,内容为该图片和原始图片的比较结果
putText(src, convertToString(src_src), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 255), 2, LINE_AA);
putText(srctest1, convertToString(src_srctest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(srctest2, convertToString(src_srctest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 255), 2, LINE_AA);
//图像的显示
namedWindow(INPUT_TITLE0, CV_WINDOW_AUTOSIZE);
namedWindow(INPUT_TITLE1, CV_WINDOW_AUTOSIZE);
namedWindow(INPUT_TITLE2, CV_WINDOW_AUTOSIZE);
//namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
imshow(INPUT_TITLE0, src);
imshow(INPUT_TITLE1, srctest1);
imshow(INPUT_TITLE2, srctest2);
waitKey(0);
return 0;
}
string convertToString(double d) {
ostringstream os;
if (os << d)
{
return os.str();
}
return "invalid conversion";
}
反向投影 – 步骤
建立直方图模型
计算待测图像直方图并映射到模型中
从模型反向计算生成图像
实现步骤与相关API
加载图片imread
将图像从RGB色彩空间转换到HSV色彩空间cvtColor
计算直方图和归一化calcHist与normalize
Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。
计算反向投影图像 - calcBackProject
// recognition.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
// 申明图像矩阵,初始化bin数目:
Mat src, hsv, hue;
int bins = 25;
void Hist_and_Backproj(int, void*);
int main(int argc, char** argv)
{
// 1. 读取输入图像并转换到HSV 空间
src = imread("3.jpg");
cvtColor(src, hsv, CV_BGR2HSV);
// 2. 使用Hue通道来创建1维直方图:
// 分离 Hue 通道。 色调在HSV颜色空间定义取值为0°到360°,实际在OpenCV中取值为0-180
hue.create(hsv.size(), hsv.depth());// 这里要传深度,传type会报错
int nchannels[] = { 0, 0 };
/* // 将输入数组的指定通道复制到输出数组的指定通道
mixChannels(
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);
*/
mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
imshow("hue", hue);
// 3. 创建 Trackbar 来输入bin的数目
char text[] = "Source image";
char* window_image = text;
namedWindow(window_image, CV_WINDOW_AUTOSIZE);
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj);
Hist_and_Backproj(0, 0);
// 现实图像
imshow(window_image, src);
// 等待用户反应
waitKey(0);
return 0;
}
void Hist_and_Backproj(int, void*)
{
MatND hist;
int histSize = MAX(bins, 2);
float hue_range[] = { 0, 180 };
const float* ranges = { hue_range };
// 计算直方图并归一化到范围[0,255]
calcHist(&hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
// 调用函数 calcBackProject 计算同一张图像的反向投影:
MatND backproj;
/*
calcBackProject ( // 反向投影
const Mat * images, // 输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
int nimages, // 输入图像的数量
const int * channels, // 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,
第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数
InputArray hist, // 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
OutputArray backProject, // 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
const float ** ranges, // 直方图中每个维度bin的取值范围
double scale = 1, // 可选输出反向投影的比例因子
bool uniform = true // 直方图是否均匀分布(uniform)的标识符,默认值true
)
*/
calcBackProject(&hue, 1, 0, hist, backproj, &ranges, 1, true);
// 显示反向投影
imshow("BackProj", backproj);
// 绘制直方图
int w = 400; int h = 400;
int bin_w = cvRound((double)w / histSize);
Mat histImg = Mat::zeros(w, h, CV_8UC3);
for (int i = 0; i < bins; i++) {
rectangle(histImg, Point(i*bin_w, h),
Point((i + 1)*bin_w, h - cvRound(hist.at<float>(i)*h / 255.0)),
Scalar(0, 0, 255), -1); //-1表示填充矩形
}
imshow("Histogram", histImg);
}
参考: