如果一幅图像的区域中显示的是一种结构纹理或者一个独特的物体,那么这个区域的直方图可以看作一个概率函数,其表现形式是某个像素数语该纹理或者物体的概率。
而反向投影就是一种记录给定图像中的像素点如何适应直方图模型像素分布的一种方法。
简单来讲,所谓的反向投影,就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在该特征的方法。
反向投影的工作原理
这里通过H-S肤色直方图来解释反向投影的工作原理。首先通过前面讲到的求出下面图像的H-S肤色直方图。
而我们要做的,就是使用模型直方图(代表手掌的皮肤色调)来检测测试图像中的皮肤区域,步骤如下:
- 对测试图像中的每个像素p(i, j),获取色彩数据并找到该色调在直方图中的bin为位置。
- 查找模型直方图中对应bin的数值。
- 将此数值存储在新的反射投影图像中,也可以先归一化直方图数值到0-255范围,这样可以直接显示反射投影图像。
- 通过对测试图像的每个像素采用以上步骤,可以得到最终的反射投影图像.
使用统计学的语言进行分析。反响投影中存储的数值代表了测试图像中该像素属于皮肤区域的概率。上图中,亮起的区域是皮肤区域的概率更大,而更暗的区域则表示是皮肤区域的概率更低。另外可以注意到,手掌内部和边缘的阴影区域影响了检测的精度。
反向投影的作用
反向投影用于在输入图像中查找与特定图像最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
反向投影的结果
反向投影的结果包含了以每个输入图像像素点为起点的直方图对比结果。可以把它看成一个二维的浮点型数组、二维矩阵,或者单通道的的浮点型图像。
计算反向投影:calcBackProject()函数
calcBackProject()函数用于计算直方图的反向投影,函数原型:
void calcBackProject(const Mat* images, int nimages, const int* channels, InputArray hist, OutputArray backProject, const float** ranges, double scale=1, bool uniform=true)
- 第一个参数:输入的数组或数据集,它们必须是相同的深度和相同的尺寸,而通道数则可以任意。
- 第二个参数:int类型的nimages,输入数组的个数,也就是第一个参数中存放了多少张图像。
- 第三个参数:需要计算的通道的索引。
- 第四个参数:输入的直方图。
- 第五个参数:目标反向投影阵列,必须是单通道。
- 第六个参数:每一个维度数组的每一维的边界阵列们可以理解为每一维的取值范围。
- 第七个参数:输出的反向投影的缩放因子,默认是1.
- 第八个参数:只是直方图是否均匀的标识符。
通道复制:mixChannels()函数
此函数由输入参数复制某通道到输出参数特定的通道中,有两个版本:
第一个版本:
void mixChannels(
const Mat* src, //输入数组,所有的矩阵必须有相同的尺寸和深度
size_t nsrcs, // 第一个参数src输入的矩阵数
Mat* dst, // 输出的数组,所有矩阵必须被初始化,大小和深度必须与src[0]相同
size_t ndsts, //第三个参数dst输入的矩阵数
const int* fromTo, //对指定的通道进行复制的数组索引
size_t npairs) //第五个参数fromTo的索引数
第二个版本:
void mixChannels(
const vector<Mat>& src, //输入数组,所有的矩阵必须有相同的尺寸和深度
const vector<Mat>& dst, // 输出的数组,所有矩阵必须被初始化,大小和深度必须与src[0]相同
const int* fromTo, //对指定的通道进行复制的数组索引
size_t npairs) //第三个参数fromTo的索引数
此函数为重排图像通道提供了比较先进的机制。其实之前接触到的split()和merge(),以及cvtColor()的某些形式,都只是该函数的一部分。
下面给出一个示例,将一个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};
int from_to[] = {0,2, 1,1, 2,0, 3,3};
mixChannels(&rgba, 1, out, 2, from_to, 4);