什么是反向投影?
- 反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式。
- 简单的讲, 所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。
- 例如, 你有一个肤色直方图 ( Hue-Saturation 直方图 ),你可以用它来寻找图像中的肤色区域:
实例讲解:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
/// 全局变量
Mat src; Mat hsv; Mat hue;
int bins = 25;
/// 函数申明
void Hist_and_Backproj(int, void*);
/** @函数 main */
int main(int argc, char** argv)
{
/// 读取图像
src = imread(argv[1], 1);
/// 转换到 HSV 空间
cvtColor(src, hsv, CV_BGR2HSV);
/// 分离 Hue 通道
hue.create(hsv.size(), hsv.depth());
int ch[] = { 0, 0 };
mixChannels(&hsv, 1, &hue, 1, ch, 1);
/// 创建 Trackbar 来输入bin的数目
char* window_image = "Source image";
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;
}
/**
* @函数 Hist_and_Backproj
* @简介:Trackbar事件的回调函数
*/
void Hist_and_Backproj(int, void*)
{
MatND hist;
int histSize = MAX(bins, 2);
float hue_range[] = { 0, 180 };
const float* ranges = { hue_range };
/// 计算直方图并归一化
calcHist(&hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
/// 计算反向投影
MatND backproj;
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);
}
imshow("Histogram", histImg);
}
代码解析:
Hue通道来创建1维直方图
hue.create( hsv.size(), hsv.depth() );
int ch[] = { 0, 0 };
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
你可以看到这里我们使用 mixChannels 来抽取 HSV图像的0通道(Hue)。 该函数接受了以下的实参:
- &hsv:一系列输入图像的数组, 被拷贝的通道的来源
- 1:输入数组中图像的数目
- &hue:一系列目的图像的数组, 储存拷贝的通道
- 1:目的数组中图像的数目
- ch[] = {0,0}:通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道。在这里,&hsv图像的Hue(0) 通道被拷贝到&hue图像(单通道)的0 通道。
- 1:通道索引对德数目
对于两幅图,我们要做的,就是使用模型直方图(代表手掌的皮肤色调)来检测测试图像中的皮肤区域。以下是检测步骤。
(1)对测试图像中的每个像素(p(j), 获取色调数据并找到该色调(hij,Sij)在直方图中的bin的位置。
(2)查询模型直方图中对应bin的数值。
(3)将此数值储存在新的反射投影图像中。也可以先归一化直方图数值到0-255范围,这样可以直接显示反射投影图像(单通道图像)。
(4)通过对测试图像中的每个像素采用以上步骤,可以得到最终的反射投影图像。
原图和测试图像相像时:
代码如下:
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; #define WINDOW_NAME "效果图" Mat src, hsv, hue; Mat temp, temp_hsv, temp_hue; int bin = 30;//直方图组距 void on_BinChange(int, void*); int main(int argc, char** argv) { src = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\1.jpg"); temp = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\1.1.jpg"); if (src.empty() || temp.empty()) { printf("Could not load image..."); return -1; } imshow("原图", src); imshow("测试图像", src); //转到HSV空间 cvtColor(src, hsv, COLOR_BGR2HSV); cvtColor(temp, temp_hsv, COLOR_BGR2HSV); //分离Hue色调通道 hue.create(hsv.size(), hsv.depth()); temp_hue.create(temp_hsv.size(), temp_hsv.depth()); int ch[] = { 0,0 }; mixChannels(&hsv, 1, &hue, 1, ch, 1); mixChannels(&temp_hsv, 1, &temp_hue, 1, ch, 1); //创建Trackbar来输入bin的数目 namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE); createTrackbar("色调组距", WINDOW_NAME, &bin, 180, on_BinChange); //进行一次初始化 on_BinChange(0, 0); imshow(WINDOW_NAME, src); waitKey(0); return 0; } void on_BinChange(int, void*) { //参数准备 MatND hist; int histSize = max(bin, 2); float hue_range[] = { 0,180 };//h通道的取值范围 const float* ranges = { hue_range }; //计算直方图并归一化 //calcHist(&hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false); //normalize(hist,hist,0,255,NORM_MINMAX,-1,Mat()); calcHist(&temp_hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false); normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat()); //计算反向投影 MatND backproj; calcBackProject(&hue, 1, 0, hist, backproj, &ranges, 1, true); imshow("反向投影图", backproj); //绘制直方图参数准备 int w = 400, h = 400; int bin_w = cvRound((double)w / histSize); Mat histImg = Mat::zeros(w, h, CV_8UC3); //绘制直方图 for (int i = 0; i<bin; 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); } imshow("直方图", histImg); }
当两幅图不同时: