模板匹配

来源:互联网 发布:圣斗士观看顺序 知乎 编辑:程序博客网 时间:2024/05/22 20:50

模板匹配的工作方式
    模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。
    假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
  (1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
  (2)用临时图像和模板图像进行对比,对比结果记为c;
  (3)对比结果c,就是结果图像(0,0)处的像素值;
  (4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;
  (5)重复(1)~(4)步直到输入图像的右下角。
    大家可以看到,直方图反向投影对比的是直方图,而模板匹配对比的是图像的像素值;模板匹配比直方图反向投影速度要快一些,但是我个人认为直方图反向投影的鲁棒性会更好。

 

模板匹配的匹配方式
    在OpenCv和EmguCv中支持以下6种对比方式:
    CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
    CV_TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
    CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
    CV_TM_SQDIFF_NORMED 归一化平方差匹配法
    CV_TM_CCORR_NORMED 归一化相关匹配法
    CV_TM_CCOEFF_NORMED 归一化相关系数匹配法
    根据我的测试结果来看,上述几种匹配方式需要的计算时间比较接近(跟《学习OpenCv》书上说的不同),我们可以选择一个能适应场景的匹配方式。

 

模板匹配的示例代码
    下面是模板匹配的C#版本代码:

//模板匹配
privatevoid btnCalc_Click(object sender, EventArgs e)
{
//输入图像
Image<Bgr, Byte> imageInput=new Image<Bgr,byte>((Bitmap)pbInput.Image);
//模板图像
Image<Bgr, Byte> imageTemplate=new Image<Bgr,byte>((Bitmap)pbTemplate.Image);
//缩放因子,更小的图像可以提高处理速度
double scale = 1d;
double.TryParse(txtScale.Text,out scale);
if (scale != 1d)
{
imageInput
= imageInput.Resize(scale, INTER.CV_INTER_LINEAR);
imageTemplate
= imageTemplate.Resize(scale, INTER.CV_INTER_LINEAR);
}
//色彩空间
string colorSpace = (string)cmbColorSpace.SelectedItem;
IImage imageInput2, imageTemplate2;
if (colorSpace =="Gray")
{
imageInput2
= imageInput.Convert<Gray, Byte>();
imageTemplate2
= imageTemplate.Convert<Gray, Byte>();
}
elseif (colorSpace =="HSV")
{
imageInput2
= imageInput.Convert<Hsv, Byte>();
imageTemplate2
= imageTemplate.Convert<Hsv, Byte>();
}
else
{
imageInput2
= imageInput.Copy();
imageTemplate2
= imageTemplate.Copy();
}
//匹配方式数组
TM_TYPE[] tmTypes=new TM_TYPE[] { TM_TYPE.CV_TM_SQDIFF, TM_TYPE.CV_TM_SQDIFF_NORMED, TM_TYPE.CV_TM_CCORR, TM_TYPE.CV_TM_CCORR_NORMED, TM_TYPE.CV_TM_CCOEFF, TM_TYPE.CV_TM_CCOEFF_NORMED };
//输出图像(匹配结果)
Image<Gray, Single>[] imageResults=new Image<Gray,float>[tmTypes.Length];
//依次执行每种匹配,并归一化结果
int i =0;
double totalTime = 0d;//总共用时
double time; //每种匹配的用时
Stopwatch sw=new Stopwatch();
txtResult.Text
+=string.Format("开始执行匹配(色彩空间:{0},缩放因子:{1})\r\n", colorSpace, scale);
foreach (TM_TYPE tmType in tmTypes)
{
sw.Start();
//模板匹配(注意:因为接口IImage中没有名为MatchTemplate的定义,所以需要进行强制转换)
//Image<Gray, Single> imageResult = imageInput2.MatchTemplate(imageTemplate2, tmType);
Image<Gray, Single> imageResult;
if (colorSpace =="Gray")
imageResult
= ((Image<Gray, Byte>)imageInput2).MatchTemplate((Image<Gray, Byte>)imageTemplate2, tmType);
elseif (colorSpace =="HSV")
imageResult
= ((Image<Hsv, Byte>)imageInput2).MatchTemplate((Image<Hsv, Byte>)imageTemplate2, tmType);
else
imageResult
= ((Image<Bgr, Byte>)imageInput2).MatchTemplate((Image<Bgr, Byte>)imageTemplate2, tmType);
sw.Stop();
time
= sw.Elapsed.TotalMilliseconds;
totalTime
+= time;
sw.Reset();
//归一化结果
CvInvoke.cvNormalize(imageResult.Ptr, imageResult.Ptr, 1d, 0d, NORM_TYPE.CV_MINMAX, IntPtr.Zero);
//找到最匹配的点,以及该点的值
double bestValue;
Point bestPoint;
FindBestMatchPointAndValue(imageResult, tmType,
out bestValue, out bestPoint);
//在最匹配的点附近画一个跟模板一样大的矩形
Rectangle rect=new Rectangle(new Point(bestPoint.X - imageTemplate.Size.Width /2, bestPoint.Y - imageTemplate.Size.Height /2), imageTemplate.Size);
imageResult.Draw(rect,
new Gray(bestValue), 2);
//保存结果图像到数组
imageResults[i]= imageResult;
i
++;
//显示结果
txtResult.Text+=string.Format("匹配方式:{0:G},用时:{1:F05}毫秒,最匹配的点:({2},{3}),最匹配的值:{4}\r\n", tmType, time, bestPoint.X, bestPoint.Y, bestValue);
}
txtResult.Text
+=string.Format("匹配结束,共用时:{0:F05}毫秒\r\n", totalTime);
//显示结果图像
pbResultSqdiff.Image= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[0]);
pbResultSqdiffNormalized.Image
= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[1]);
pbResultCcorr.Image
= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[2]);
pbResultCcorrNormalized.Image
= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[3]);
pbResultCcoeff.Image
= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[4]);
pbResultCcoeffNormalized.Image
= ImageConverter.ImageSingleToBitmap<Gray>(imageResults[5]);
//释放资源
imageInput.Dispose();
imageTemplate.Dispose();
imageInput2.Dispose();
imageTemplate2.Dispose();
foreach (Image<Gray, Single> imageResult in imageResults)
imageResult.Dispose();
}

//找到最匹配的点,以及该点的值
privatevoid FindBestMatchPointAndValue(Image<Gray, Single> image, TM_TYPE tmType, outdouble bestValue, out Point bestPoint)
{
bestValue
= 0d;
bestPoint
=new Point(0,0);
double[] minValues, maxValues;
Point[] minLocations, maxLocations;
image.MinMax(
out minValues, out maxValues, out minLocations, out maxLocations);
//对于平方差匹配和归一化平方差匹配,最小值表示最好的匹配;其他情况下,最大值表示最好的匹配
if (tmType == TM_TYPE.CV_TM_SQDIFF|| tmType== TM_TYPE.CV_TM_SQDIFF_NORMED)
{
bestValue
= minValues[0];
bestPoint
= minLocations[0];
}
else
{
bestValue
= maxValues[0];
bestPoint
= maxLocations[0];
}
}
显示结果图像
    模板匹配和直方图反向投影生成的结果图像都是32位浮点型单通道图像。如果用C/C++,可以很方便的用OpenCv中的cvShowImage函数来显示;如果用.net,因为EmguCv中将32位浮点图像转换成8位位图的方法有些小问题,我们要自己编写一段转换的代码,然后再显示。

///<summary>
/// 将任意浮点型图像转换成Byte图像;
/// 本转换函数对浮点型图像的具体像素值没有要求,自动将值缩放到0~255之间。
///</summary>
///<typeparam name="TColor">图像的色彩空间</typeparam>
///<param name="source">浮点型图像</param>
///<returns>返回Byte型图像</returns>
publicstatic Image<TColor, Byte> ImageSingleToByte<TColor>(Image<TColor, Single> source)
where TColor : struct, IColor
{
Image
<TColor, Byte> dest =new Image<TColor, Byte>(source.Size);
//得到源图像的最小和最大值
double[] minVal, maxVal;
Point[] minLoc, maxLoc;
source.MinMax(
out minVal, out maxVal, out minLoc, out maxLoc);
double min = minVal[0];
double max = maxVal[0];
for (int i =1; i < minVal.Length; i++)
{
min
= Math.Min(min, minVal[i]);
max
= Math.Max(max, maxVal[i]);
}
//得到缩放比率和偏移量
double scale =1.0, shift =0.0;
scale
= (max== min)?0.0 : 255.0/ (max- min);
shift
= (scale==0)? min :-min* scale;
//缩放图像,并浮点图像缩放到256级的灰度
CvInvoke.cvConvertScaleAbs(source.Ptr, dest.Ptr, scale, shift);
return dest;
}

///<summary>
/// 将任意浮点型图像转换成每通道8位的Bitmap;
/// 本转换函数对浮点型图像的具体像素值没有要求,自动将值缩放到0~255之间。
///</summary>
///<typeparam name="TColor">图像的色彩空间</typeparam>
///<param name="source">浮点型图像</param>
///<returns>返回每通道8位的Bitmap</returns>
publicstatic Bitmap ImageSingleToBitmap<TColor>(Image<TColor, Single> source)
where TColor : struct, IColor
{
Image
<TColor, Byte> dest = ImageSingleToByte<TColor>(source);
Bitmap bitmap
= dest.Bitmap;
dest.Dispose();
return bitmap;
}

0 0