二十五、直方图反向投影
1、反向投影(Back Projection)
- 反向投影是反映直方图在目标图像中的分布情况
- 简单点说就是用直方图模型去目标图像中寻找是存在否相似的对象。通常用HSV色彩空间的HS两个通道直方图模型。
- 具体操作是先得到一张图像的直方图信息,然后遍历原图像的每一个像素,如果这个像素值在直方图的某个bin下,就用这个bin出现的频次去代替这个像素值。这样划分了多少个bin,新生成的图像就有多少个像素值种类(而不是原来的0-255的256个种类)。比如说将0-256分成16份(16个bin),计算直方图得到原图像(0,0)位置处的像素值为250,落在了第16个bin下,而原图像直方图在这个bin下的值(频次)为56(经过归一化),则反向投影图像中(0,0)位置处的像素值为56。
- 用处举例:从某张图像中找出相似对象。假如拍摄的两张图片都含有同一目标物体(如两只手的图像)。可以用其中一张图像提取出的直方图信息,去遍历另一张图片的像素,若遍历到目标像素值,则它可以被替换为第一张图片出现该像素值的个数,如果这张图片中含有相同目标,会被显示出来。
例如用第一张图片的直方图信息去遍历第二张图片生成反投影信息:(左侧为原图,右侧为测试图)
输出结果:
2、步骤
- 建立直方图模型
- 计算待测图像直方图并映射到模型中
- 从模型反向计算生成图像
详细理解原理可参考这篇博客
3、相关API
- 加载图片
imread
- 将图像从RGB空间转换到HSV色彩空间
cvtColor
因为BGR直方图我们只能三个通道分别计算,计算后也没法合在一起,也就是不能把图片作为一个整体来比较,而H-S就可以将图片作一个整体对比,所以这里用H-S直方图来作对比。 - 计算直方图并归一化
calcHist
与normlize
-
Mat
与MatND
,其中Mat表示二维数组,MatND表示三维或者多维数组,此处均可以用Mat表示 - 计算反向投影图像
calcBackProject
calcBackProject(
const Mat* images,//输入图像的指针,可以传入多张图像
int nimages,//输入图像的数量
const int* channels,//用于计算反向投影的通道列表,通道数必须与直方图维度相匹配
InputArray hist,//输入的直方图
OutputArray backProject,//目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
const float ranges**,//直方图中每个维度bin的取值范围
double scale=1,//可选输出反向投影的比例因子
bool uniform=true//直方图是否均匀分布(uniform)的标识符,有默认值true
)
示例代码:(直方图反向投影与绘制)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
Mat src; Mat hsv; Mat hue;
int bins = 12;
void Hist_And_Projection(int, void*);
int main(int argc, char* argv) {
src = imread("添加图片路径");
if (src.empty()) {
cout << "could not load image..." << endl;
return -1;
}
const char* window_image = "input image";
namedWindow(window_image, WINDOW_AUTOSIZE);
namedWindow("BackPro", WINDOW_AUTOSIZE);
namedWindow("Histogram", WINDOW_AUTOSIZE);
cvtColor(src, hsv, COLOR_BGR2HSV);
hue.create(hsv.size(), hsv.depth());//创建一个hue图像,尺寸深度和hsv相同
int nchannels[] = { 0,0 };//映射关系
mixChannels(&hsv, 1, &hue, 1, nchannels, 1); //mixChannels主要就是把输入的矩阵(或矩阵数组)(hsv)的某些通道拆分复制给对应的输出矩阵(或矩阵数组)(hue)的某些通道中,其中的对应关系就由fromTo参数(nchannels)指定.
//把hsv中的一个通道0矩阵输出给hue的一个通道0矩阵,通过nchannels映射关系,即0通道的映射
createTrackbar("Histogram Bins:", window_image, &bins , 180, Hist_And_Projection);//控制bin的划分
Hist_And_Projection(0, 0);
imshow(window_image, src);
waitKey(0);
return 0;
}
void Hist_And_Projection(int, void*) {
//计算直方图与归一化
float range[] = { 0, 180 };
const float *histRanges = { range };
Mat h_hist;
calcHist(&hue, 1, 0, Mat(), h_hist,1, &bins, &histRanges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());//出现的频次应在0-255之间,因为要用这个频次做为反投影图像的像素值
//反向投影
Mat backProjectimage;
calcBackProject(&hue, 1, 0, h_hist, backProjectimage, &histRanges, 1, true);
imshow("BackPro", backProjectimage);
//绘制直方图
int hist_h = 400;
int hist_w = 400;
Mat hist_image(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//创建一张直方图图像
int bin_w = (hist_w) / bins;//直方图bin的宽度
for (int i = 1; i <= bins; i++) {
rectangle(hist_image,
Point((i - 1)*bin_w, hist_h - h_hist.at<float>(i - 1) / 255 * 400), //矩形框的第一个点,直方图的左上角顶点。因为之前将其归一化到0-255之间,而整张图片的高度为400,所以y的值最后要在图像的尺度下表示
Point(i*bin_w, hist_h), //矩形框的第二个点,直方图的右下角顶点
Scalar(0, 0, 255), -1);
}
imshow("Histogram", hist_image);
return;
}
输出图像效果: