什么是反向投影?
- 反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式。
- 所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。
反向投影的原理
- 假设你已经通过下图得到一个肤色直方图(Hue-Saturation), 旁边的直方图就是模型直方图 ( 代表手掌的皮肤色调).你可以通过掩码操作来抓取手掌所在区域的直方图。
- 我们要做的就是使用 模型直方图 (代表手掌的皮肤色调) 来检测测试图像中的皮肤区域。以下是检测的步骤:
- 对测试图像中的每个像素 ( ),获取色调数据并找到该色调( )在直方图中的bin的位置。
- 查询 模型直方图 中对应的bin - - 并读取该bin的数值。
- 将此数值储存在新的图像中(BackProjection)。 你也可以先归一化 模型直方图 ,这样测试图像的输出就可以在屏幕显示了。
- 通过对测试图像中的每个像素采用以上步骤, 我们得到了下面的 BackProjection 结果图:
- 使用统计学的语言, BackProjection 中储存的数值代表了测试图像中该像素属于皮肤区域的 概率 。比如以上图为例, 亮起的区域是皮肤区域的概率更大(事实确实如此),而更暗的区域则表示更低的概率(注意手掌内部和边缘的阴影影响了检测的精度)。
相关实现代码
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/types_c.h"
#include <opencv2/core/core.hpp>
#include <iostream>
using namespace cv;
using namespace std;
Mat hsv, hue;
int bins = 25;
void Hist_and_Backproj(int, void*);
int main()
{
// 读取图像
Mat src = imread("palm.png", 1);
if (!src.data)
{
printf("No data!--Exiting the program \n");
return -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, 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 };
//计算直方图并进行归一化处理
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);
}
关键代码讲解
- 申明图像矩阵,初始化bin数目
Mat src; Mat hsv; Mat hue;
int bins = 25;
- 读取输入图像并转换到HSV格式
src = imread( argv[1], 1 );
cvtColor( src, hsv, CV_BGR2HSV );
- 使用Hue通道来创建1维直方图
hue.create( hsv.size(), hsv.depth() );
int ch[] = { 0, 0 };
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
- 创建Trackbar方便用户输入bin数目。 Trackbar的任何变动将会调用函数 Hist_and_Backproj 。
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 函数: 初始化函数 calcHist 需要的实参, bin数目来自于 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() );
- 调用函数 calcBackProject 计算同一张图像的反向投影
MatND backproj;
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
- 显示 backproj
imshow( "BackProj", backproj );
- 显示1维 Hue 直方图
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 );
函数详解
- mixChannels 函数
- &hsv: 一系列输入图像的数组, 被拷贝的通道的来源
- 1: 输入数组中图像的数目
- &hue: 一系列目的图像的数组, 储存拷贝的通道
- 1: 目的数组中图像的数目
- ch[] = {0,0}: 通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道。在这里,&hsv图像的Hue(0) 通道被拷贝到&hue图像(单通道)的0 通道。
- 1: 通道索引对的数目
- 函数原型:void normalize(InputArray src,OutputArraydst, double alpha = 1, double beta = 0, intnorm_type = NORM_L2, int dtype = -1, InputArray mask = noArray() )
- 参数详解:
src: 输入数组
dst: 输出数组,支持原地运算
alpha: range normalization模式的最小值
beta: range normalization模式的最大值,不用于norm normalization(范数归一化)模式。
normType:归一化的类型,可以有以下的取值:
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
NORM_INF:此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
NORM_L1 : 归一化数组的L1-范数(绝对值的和)
NORM_L2: 归一化数组的(欧几里德)L2-范数
dtype : dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype).
mask: 操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。
- calcHist函数
- 函数原型:C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
- 参数详解:
onst Mat* images:输入图像
int nimages:输入图像的个数
const int* channels:需要统计直方图的第几通道
InputArray mask:掩膜,,计算掩膜内的直方图 ...Mat()
OutputArray hist:输出的直方图数组
int dims:需要统计直方图通道的个数
const int* histSize:指的是直方图分成多少个区间,就是 bin的个数
const float** ranges: 统计像素值得区间
bool uniform=true::是否对得到的直方图数组进行归一化处理
bool accumulate=false:在多个图像时,是否累计计算像素值得个数
实验效果
实验代码下载
https://download.csdn.net/download/lykymy/11295315