理论
什么是背投影?
- 反投影是一种记录给定图像的像素与直方图模型中的像素分布的契合程度的方法。
- 为了使其更简单:对于反向投影,您计算特征的直方图模型,然后使用它在图像中找到该特征。
- 应用示例:如果您有肤色直方图(例如,色相饱和度直方图),那么您可以使用它在图像中查找肤色区域:
它是如何工作的?
- 我们用皮肤的例子来解释这一点:
- 假设您根据下图获得了皮肤直方图(色相饱和度)。此外,直方图将成为我们的模型直方图(我们知道它代表了肤色样本)。您应用了一些蒙版来仅捕获皮肤区域的直方图:
- 现在,让我们想象您获得另一张如下所示的手部图像(测试图像):(带有其相应的直方图):
- 我们想要做的是使用模型直方图(我们知道它代表肤色)来检测测试图像中的皮肤区域。步骤如下
- 在我们的测试图像的每个像素中(即页(我,杰)),收集数据并找到该像素对应的 bin 位置(即(时长我,杰,s我,杰))。
- 在相应的箱中查找模型直方图-(时长我,杰,s我,杰)- 并读取 bin 值。
- 将此 bin 值存储在新图像 ( BackProjection ) 中。此外,您可以考虑先对模型直方图进行归一化,这样测试图像的输出就对您可见了。
- 应用上述步骤,我们得到测试图像的以下反投影图像:
- 从统计角度来看,BackProjection中存储的值表示基于我们使用的模型直方图,测试图像中像素属于皮肤区域的概率。例如,在我们的测试图像中,较亮的区域更有可能是皮肤区域(实际上也是如此),而较暗的区域则概率较小(请注意,这些“暗”区域属于带有阴影的表面,这反过来会影响检测)。
代码
C++JavaPython
- 这个程序是做什么的?
- 加载图片
- 将原始图像转换为 HSV 格式,并仅分离用于直方图的色调通道(使用 OpenCV 函数cv::mixChannels)
- 让用户输入用于计算直方图的箱数。
- 计算直方图(如果箱体发生变化则更新它)和同一幅图像的反投影。
- 在窗口中显示反投影和直方图。
- 可下载代码:
- 单击此处获取基本版本(本教程中说明)。
- 对于稍微复杂一点的东西(使用 HS 直方图和 FloodFill 来定义皮肤区域的蒙版),你可以查看改进的演示
- ...或者您可以随时查看样本中的经典camshiftdemo。
- 代码一览:
#include " opencv2/imgproc.hpp "#include " opencv2/imgcodecs.hpp "#包括“ opencv2/highgui.hpp ”#包括 <iostream>使用命名空间cv;使用命名空间std;无光泽色调;int箱数 = 25;无效Hist_and_Backproj(int,void *);int main(int argc,char * argv []){CommandLineParser解析器(argc,argv,“{@input |Back_Projection_Theory0.jpg|输入图像}”);样本::addSamplesDataSearchSubDirectory( “doc/tutorials/imgproc/histograms/back_projection/images” );if ( 源文件为空() ){cout << "无法打开或找到图像!\n" << endl;cout << "用法:" << argv[0] << " <输入图像>" << endl;返回-1;}垫hsv;cvtColor(src, hsv, COLOR_BGR2HSV );int ch [] = { 0, 0 };混合通道(&hsv, 1,&hue, 1,ch, 1 );const char * window_image = "源图像" ;命名窗口(窗口图像);createTrackbar( "* 色相箱:",window_image,&bins,180,Hist_and_Backproj);历史和反向投影(0,0);imshow(窗口图像, src );// 等到用户退出程序等待键();返回0;}void Hist_and_Backproj( int,void * ){int histSize = MAX ( 箱数, 2 ) ;浮点色相范围[] = { 0, 180 };const float * 范围[] = { 色调范围 };垫历史;Mat反向项目;calcBackProject ( &hue, 1, 0, hist, backproj, ranges, 1, true );imshow ( “BackProj” , backproj );int w = 400,h = 400;int bin_w = cvRound ( ( double ) w / histSize ) ;对于(int i = 0; i < bins; i++){标量(0,0,255),已填充);}imshow ( "直方图" , histImg );}
解释
C++JavaPython
-
读取输入图像:
CommandLineParser解析器(argc,argv,“{@input |Back_Projection_Theory0.jpg|输入图像}”);样本::addSamplesDataSearchSubDirectory( “doc/tutorials/imgproc/histograms/back_projection/images” );if ( 源文件为空() ){cout << "无法打开或找到图像!\n" << endl;cout << "用法:" << argv[0] << " <输入图像>" << endl;返回-1;} -
将其转换为 HSV 格式:
-
对于本教程,我们将仅使用色调值作为一维直方图(如果您想使用更标准的 HS 直方图,请查看上面链接中的更精美的代码,以产生更好的结果):
- 如您所见,我们使用函数cv::mixChannels从 hsv 图像中仅获取通道 0(色调)。它获取以下参数:
- &hsv:将从中复制通道的源数组
- 1:源阵列的数量
- &hue:复制通道的目标数组
- 1:目标数组的数量
- ch[] = {0,0}:指示如何复制通道的索引对数组。在本例中,&hsv 的 Hue(0) 通道被复制到 &hue 的 0 通道(1 通道)
- 1:索引对的数量
-
创建一个 Trackbar 供用户输入 bin 值。Trackbar 上的任何变化都意味着对Hist_and_Backproj回调函数的调用。
const char * window_image = "源图像" ;命名窗口(窗口图像);createTrackbar ( "* 色相箱: ",window_image,&bins,180,Hist_and_Backproj );历史和反向投影(0,0); -
显示图像并等待用户退出程序:
-
Hist_and_Backproj 函数:初始化cv::calcHist所需的参数。箱数来自 Trackbar:
-
计算直方图并将其标准化为范围[0,255]
-
通过调用函数cv::calcBackProject获取同一图像的反向投影
Mat反向项目;calcBackProject ( &hue, 1, 0, hist, backproj, ranges, 1, true ); - 所有参数都是已知的(与计算直方图所用的相同),只需添加 backproj 矩阵,它将存储源图像(&hue)的反向投影
-
显示背景项目:
imshow ( “BackProj” , backproj ); -
绘制图像的一维色调直方图:
结果
以下是使用示例图像的输出(猜猜是什么?另一只手)。您可以调整 bin 值,然后观察它如何影响结果: