直方图的反向投影(转自:http://ww…

来源:互联网 发布:macbook用office软件 编辑:程序博客网 时间:2024/05/19 13:59

直方图的反向投影
   现在说说直方图的反向投影,我觉得这是直方图中最难的部分,我看了跟cvCalcBackProjectPatch相关的读书章节、中文参考及英文帮助,还是不明白......而网上居然没有使用cvCalcBackProjectPatch的例程(当然本文写成之后就有例程了)。前天晚上在半梦半醒之间进入了冥想状态,突然开窍了,然而在试验的时候还是遇到了不少问题。
1.反向投影的作用是什么?
   反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
2.反向投影如何查找(工作)?
   查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。

假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。

 直方图反向投影示意图

(本图片引用自http://www.opencv.org.cn)
3.反向投影的结果是什么?
   反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。
4.特殊情况怎么样?
   如果输入图像和模板图像一样大,那么反向投影相当于直方图对比。如果输入图像比模板图像还小,直接罢工~~。
5.使用时有什么要注意的地方?
   需要注意的地方比较多,我们对照反向投影函数来说:
    voidcvCalcBackProjectPatch(
       IplImage** image,  /输入图像:是一个单通道图像数组,而非实际图像/
       CvArr*dst,        /输出结果:是一个单通道32位浮点图像,它的宽度为W-w+1,高度为H-h+1,这里的W和H是输入图像的宽度和高度,w和h是模板图像的宽度和高度/
       CvSize patch_size,  /模板图像的大小:宽度和高度/
       CvHistogram* hist, /模板图像的直方图:直方图的维数和输入图像的个数相同,并且次序要一致;例如:输入图像包含色调和饱和度,那么直方图的第0维是色调,第1维是饱和度/
       intmethod,        /对比方式:跟直方图对比中的方式类似,可以是:CORREL(相关)、CHISQR(卡方)、INTERSECT(相交)、BHATTACHARYYA/
       floatfactor       /归一化因子,一般都设置成1,否则很可能会出错;中文、英文以及各路转载的文档都错了,这个参数的实际类型是double,而非float,我看了源代码才搞定这个地方/
    );
   还有最需要注意的地方:这个函数的执行效率非常的低,在使用之前尤其需要注意图像的大小,直方图的维数,对比方式。如果说对比单个直方图对现在的电脑来说是清风拂面,那么反向投影是狂风海啸。

对于1010x1010的RGB输入图像,10x10的模板图像,需要生成1百万次3维直方图,对比1百万次3维直方图。
直方图反向投影的示例如下:
计算直方图的反向投影
//计算反向投影
private void btnCalcBackProjectPatch_Click(object sender,EventArgs e)
{
Stopwatch sw
=new Stopwatch();
sw.Start();
//定义变量
Image<Bgr, Byte> imageSource = newImage<Bgr, byte>((Bitmap)pbSource.Image);
double scale =GetScale(); //用一个缩放因子减小源图像的大小,可以极大的提高处理速度
Image<Bgr, Byte> imageSource2 = imageSource.Resize(scale,INTER.CV_INTER_LINEAR);
Image
<Bgr, Byte> imageTarget = newImage<Bgr, byte>((Bitmap)pbTarget.Image);
Image
<Bgr, Byte> imageTarget2 = imageTarget.Resize(scale,INTER.CV_INTER_LINEAR);
Image
<Gray, Single> imageDest = null;
Size patchSize
=imageTarget2.Size;
string colorSpace = (string)cmbColorSpace.SelectedItem; //色彩空间的选择对处理速度也有很大的影响,更少的色彩空间数目能极大的提高处理速度
double normalizeFactor = 1d;//归一化因子,一般设置为1,且必须为double类型
Image<Gray, Byte>[] imagesSource;
int[]bins;
RangeF[] ranges;
string[]captions;
Color[] colors;
//得到源图像需要参与反向投影的各色彩图像集合
FormProcessHist.GetImagesBinsAndRanges(imageSource2,colorSpace, outimagesSource, out bins, out ranges, out captions, out colors);
//计算目标图像的直方图
DenseHistogram histTarget = CalcHist(imageTarget2.Bitmap);
//反向投影
HISTOGRAM_COMP_METHOD compareMethod= GetHistogramCompareMethod();
try
{
imageDest
= BackProjectPatch<Byte>(imagesSource, patchSize, histTarget,compareMethod, (float)normalizeFactor); //DenseHistogram类的方法BackProjectPath不能用,这里自己实现了一个类似的方法
}
catch(Exception ex)
{
txtResult.Text
+=string.Format("在执行反向投影时发生错误,错误描述:{0},错误源:{1}\r\n",ex.Message, ex.Source);
}
//显示结果
if (imageDest != null)
pbResult.Image
=imageDest.Bitmap;
sw.Stop();
txtResult.Text
+=string.Format("直方图反向投影用时:{0:F05}毫秒,对比方式:{1:G},({2})\r\n",sw.Elapsed.TotalMilliseconds, compareMethod,GetOptions());
//释放资源
imageSource.Dispose();
imageSource2.Dispose();
imageTarget.Dispose();
imageTarget2.Dispose();
if (imageDest != null)
imageDest.Dispose();
foreach (Image<Gray, Byte> image in imagesSource)
image.Dispose();
histTarget.Dispose();
}

 

在左图(输入图像)中查找特定人脸(模板图像),结果如右图,中间亮白色的区域为最匹配区域 

 

   如果您跟我一样在.net下使用EmguCv,那么恭喜你,还有需要解决的问题。EmguCv的直方图反向投影部分有几处错误,不能直接使用。错误列表如下:
   (1)CvInvoke.cvCalcBackProjectPatch方法不能使用,会出现“在dll中找不到入口”的异常;
   (2)DenseHistogram.BackProjectPatch方法不能使用,也会出现“在dll中找不到入口”的异常;
   (3)分明将归一化因子设置成1,但是依然提示我“归一化因子错误,如果不确定归一化因子,请将其设置为1”;
   (4)返回的结果图像类型错误。
   通过查看OpenCv和EmguCv的源代码及输出dll文件,确定两者的问题所在:
   (1)在OpenCv中只有一个名为cvCalcBackProjectPatch的宏,而不是内联函数,宏在编译成dll之后当然不会变成输出API了;
   (2)OpenCv的文档中将归一化因子的类型错写成float类型,如果在.net中用P/INVOKE调用,不会有编译错误,但是会有无法解决的运行时错误;
   (3)既然OpenCv错了,EmguCv也将错就错,没有调用正确的函数cvCalcArrBackProjectPatch。
   在.net中使用直方图的反向投影可以用下面的正确方式:

.net中的直方图反向投影P/INVOKE

[DllImport(
"cv200.dll",EntryPoint ="cvCalcArrBackProjectPatch")]
public static extern void cvCalcBackProjectPatch(
IntPtr[] images,
IntPtr dst,
Size patchSize,
IntPtr hist,
HISTOGRAM_COMP_METHOD method,
double factor);

/// <summary>
/// 计算直方图的反向投影
/// </summary>
/// <paramname="srcs">Source images, all are of the same size andtype</param>
/// <paramname="factor">Normalization factor for histograms, will affectnormalization scale of destination image, pass 1. if unsure.</param>
/// <paramname="patchSize">Size of patch slid though the sourceimages.</param>
/// <paramname="method">Comparison method, passed tocvCompareHist.</param>
/// <typeparamname="TDepth">The type of depth of the image</typeparam>
/// <returns>Destinationback projection image of the same type as the sourceimages</returns>
public Image<Gray, Single> BackProjectPatch<TDepth>(Image<Gray, TDepth>[] srcs, Size patchSize, DenseHistogram hist,HISTOGRAM_COMP_METHOD method, double factor) where TDepth: new()
{
Debug.Assert(srcs.Length
== hist.Dimension, "直方图的维数和源图像的数目必须相同");

IntPtr[] imgPtrs
=
Array.ConvertAll
<Image<Gray, TDepth>, IntPtr>(
srcs,
delegate(Image<Gray, TDepth> img) { return img.Ptr; });
Size size
= srcs[0].Size;
size.Width
=size.Width - patchSize.Width + 1;
size.Height
=size.Height - patchSize.Height + 1;
Image
<Gray, Single> res =new Image<Gray, float>(size);
cvCalcBackProjectPatch(imgPtrs, res.Ptr, patchSize, hist.Ptr,method, factor);
return res;
}

已经将发现的上述错误提交给OpenCv及EmguCv的BUG LIST,希望能尽快解决。

0 0