9.4 反向投影(back projection)
9.4.1 反向投影原理
1.基本思想:
反向投影中储存的数值代表了图像中该像素属于区域的概率,计算某一特征的直方图模型,使用模型寻找图像中存在的该特征
2.基本原理:
(1)对图像中每个像素(p(i,j)),获取色调数据并找到该色调/饱和度在直方图中的bin的位置
(2)查询模型直方图中对应的bin的数值
(3)将此数值存储在新的反射投影图像中。也可以先归一化直方图数值到0-255范围内,这样可以直接显示反射投影图像
(4)使用统计学语言进行分析,反向投影中储存的数值代表了图像中该像素属于区域的概率
3.作用:
在输入图像中查找与特定图像最匹配的点或区域,即定位模板图像出现在输入图像的位置
4.结果:
包含了以每个输入图像像素点为起点的直方图对比结果的二维浮点型数组/二维矩阵/单通道浮点型图像
9.4.2 计算反向投影:calcBackProject()函数
1.作用:
计算直方图反向投影
2.函数原型:
void calcBackProject(const Mat* image, int nimages, const int* channels, InputArray hist, OutputArray backProject, const float** ranges, double scale=1, bool uniform=true)
3.参数说明:
(1)输入数组(集)
(2)输入数组个数
(3)需要统计的通道(dim)索引,第一个数组通道从0到images[0].channels()-1,第二个数组通道从images[0].channels()计算到images[0].channels()+images[1].channels()-1。
(4)输入直方图
(5)目标反向投影阵列,单通道且与image[0]大小深度相同
(6)表示每一个维度数组的每一维的边界阵列,即每一维数值的取值范围
(7)输出方向投影可选的缩放因子,默认1
(8)指示直方图是否均匀的标识符,默认true
9.4.3 通道复制:mixChannels()函数
1.作用:
由输入参数复制到某通道到输出参数特定的通道中,实现图像通道重排
2.函数原型:
(1)void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs)
(2)void mixChannels(const vector<Mat>& src, vector<Mat>& dst, const int* fromTo, size_t npairs)
3.参数说明(1):
(1)输入数组
(2)src输入的矩阵数
(3)输出的数组,所有矩阵必须被初始化,且大小和深度必须与src[0]相同
(4)dst输入的矩阵数
(5)对指定的通道进行复制的数组索引
(6)参数fromTo的索引数
4.参数说明(2):
(1)输入的矩阵向量
(2)输出的矩阵向量,所有矩阵必须被初始化,且大小和深度必须与src[0]相同
(3)对指定的通道进行复制的数组索引
(4)参数fromTo的索引数
5.示例:
将一个4通道RGBA图像转化为3通道BGR和一个单独的Alpha通道图像
Mat rgba(100, 100, CV_8UC4, Scalar(1, 2, 3, 4));
Mat bgr(rgba.rows, rgba.cols, CV_8UC3);
Mat alpha(rgba.rows, rgba.cols, CV_8UC1);
//组成矩阵数组操作
Mat out[] = { bgr,alpha };
//说明:将rgba[0]->bgr[2],rgba[1]->bgr[1],将rgba[0]->bgr[0],rgba[3]->alpha[0]
int from_to[] = { 0,2,1,1,2,0,3,3 };
mixChannels{ &rgba,1,out,2,from_to,4 };
9.4.4 综合示例
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
//定义辅助宏
#define WINDOW_NAME1 "【原始图】"
#define WINDOW_NAME2 "【反向投影图】"
//全局变量
Mat g_srcImage, g_hsvImage, g_hueImage;
int g_bins = 30;//直方图组距
//全局函数
void on_BinChange(int, void*);
int main()
{
//【1】载入原图,转换到HSV空间
g_srcImage = imread("1.jpg", 1);
if (!g_srcImage.data)
{
printf("载入原图失败~!\n");
return false;
}
cvtColor(g_srcImage, g_hsvImage, COLOR_BGR2HSV);
//【2】分离Hue色调通道
g_hueImage.create(g_hsvImage.size(), g_hsvImage.depth());
int ch[] = { 0,0 };
mixChannels(&g_hsvImage, 1, &g_hueImage, 1, ch, 1);
//【3】创建Trackbar来输入bin的数目
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
createTrackbar("色调组距", WINDOW_NAME1, &g_bins, 180, on_BinChange);
on_BinChange(0, 0);
//【4】显示原图
imshow(WINDOW_NAME1, g_srcImage);
waitKey(0);
return 0;
}
void on_BinChange(int, void*)
{
//【1】参数准备
MatND hist;
int histSize = MAX(g_bins, 2);
float hue_range[] = { 0,180 };
const float* ranges = { hue_range };
//【2】计算直方图并归一化
calcHist(&g_hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
//【3】计算反向投影
MatND backproj;
calcBackProject(&g_hueImage, 1, 0, hist, backproj, &ranges, 1, true);
//【4】显示反向投影
imshow(WINDOW_NAME2, backproj);
//【5】绘制直方图的参数准备
int w = 400, h = 400;
int bin_w = cvRound((double)w / histSize);
Mat histImg = Mat::zeros(w, h, CV_8UC3);
//【6】绘制直方图
for (int i = 0; i < g_bins; i++)
{
rectangle(histImg, Point(i*bin_w, h), Point((i + 1)*bin_w, h - cvRound(hist.at<float>(i)*h / 255.0)), Scalar(100, 123, 255), -1);
}
//【7】显示直方图窗口
imshow("直方图", histImg);
}
运行效果: