模板匹配(Match Template)
前言??? 模板匹配是在圖像中尋找目標的方法之一。Come On, Boy.我們一起來看看模板匹配到底是怎么回事。
模板匹配的工作方式
??? 模板匹配的工作方式跟直方圖的反向投影基本一樣,大致過程是這樣的:通過在輸入圖像上滑動圖像塊對實際的圖像塊和輸入圖像進行匹配。
??? 假設我們有一張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#版本代碼:
//模板匹配private void 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>();
}
else if (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);
else if (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();
}
//找到最匹配的點,以及該點的值
private void FindBestMatchPointAndValue(Image<Gray, Single> image, TM_TYPE tmType, out double 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];
}
} //模板匹配private void 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>();}else if (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);else if (colorSpace == "HSV")imageResult = ((Image<Hsv, Byte>)imageInput2).MatchTemplate((Image<Hsv, Byte>)imageTemplate2, tmType);elseimageResult = ((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();}//找到最匹配的點,以及該點的值private void FindBestMatchPointAndValue(Image<Gray, Single> image, TM_TYPE tmType, out double 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>
public static 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>
public static 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;
} /// <summary>/// 將任意浮點型圖像轉換成Byte圖像;/// 本轉換函數對浮點型圖像的具體像素值沒有要求,自動將值縮放到0~255之間。/// </summary>/// <typeparam name="TColor">圖像的色彩空間</typeparam>/// <param name="source">浮點型圖像</param>/// <returns>返回Byte型圖像</returns>public static 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>public static 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;}
??????? 左上是輸入圖像,左中是模板圖像,右邊是各種匹配方式的結果(相關匹配的結果明顯不正確)
模板匹配和直方圖反向投影的效率??? 總的來說,模板匹配和直方圖反向投影的效率都不高。在我的機器上,在1136*852大小的輸入圖像上匹配104*132的大小的模板圖像(都是單通道灰度圖像),大約需要700毫秒;而直方圖反向投影大約需要75000毫秒(1.25分鐘)。看來還需要繼續學習,尋找更好的處理方法。
??? 另一方面,通過搜索OpenCv的源代碼,發現OpenCv基本上沒有使用并行計算。如果學習完之后,還有時間和熱情,我準備嘗試優化下OpenCv的并行計算;如果.net 4.0正式版推出了,也可以選擇在這一方面做點優化。
?轉:https://www.cnblogs.com/xrwang/archive/2010/02/05/MatchTemplate.html
總結
以上是生活随笔為你收集整理的模板匹配(Match Template)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Emgu介绍
- 下一篇: 潮爆大状粤语全集(潮爆大状粤语)