Cv2.MatchTemplate()方法在模板图像与测试图像分辨率不同的情况下会失效,因为模板匹配的原理是将模板从测试图片中从左到右,从上到下依次滑动来找到匹配度最高的地方;
因此,为了实现多尺度的模板匹配,需要对其进行改进;
主要思路:循环缩放模板图像的大小再与测试图像进行匹配(很暴力)。
实现的核心代码如下(c#):
cs
public void MultiSixeMatch(String sourceImagePath, bool isSaveResult, int samllResizeLoop = 20, double samllResize = 0.02, int largeResizeLoop = 40, double largeResize = 0.02, bool earlyStop = true, double stopConfidence = 0.8)
{
Mat testPic = Cv2.ImRead(sourceImagePath, ImreadModes.Color);
//用来存储每次匹配的结果:匹配信任度、最大匹配位置
double tempConfidence = double.NegativeInfinity;
Point tempMaxLocal = new Point();
// 缩放模板 当前模版为最大尺寸,每次循环缩小2%,循环20次
for (int index = -samllResizeLoop; index < 0; index++)
{
Mat tempTemplate = templatePic.Clone();
int newRows = (int)(templatePic.Rows + index * samllResize * templatePic.Rows);
int newCols = (int)(templatePic.Cols + index * samllResize * templatePic.Cols);
// resize模板图像大小
Cv2.Resize(tempTemplate, tempTemplate, new Size(newCols, newRows));
// 将得到的小模板进行与测试图像进行匹配
Mat matchResult = new Mat();
Cv2.MatchTemplate(testPic, tempTemplate, matchResult, TemplateMatchModes.CCoeffNormed);
// 解析模板匹配的结果 最大匹配区域以及匹配度
Cv2.MinMaxLoc(matchResult, out _, out tempConfidence, out _, out tempMaxLocal);
Rect tempRect = new Rect(tempMaxLocal, tempTemplate.Size());
// 判断当前匹配信任度 如果高于阈值 则结束匹配
if(earlyStop && tempConfidence > stopConfidence)
{
updateResult(index, tempMaxLocal, tempRect, tempConfidence);
saveResult(testPic, sourceImagePath, isSaveResult);
showPicture(testPic);
return;
}
// 记录当前最好的结果
if (tempConfidence > confidence)
{
updateResult(index, tempMaxLocal, tempRect, tempConfidence);
}
}
// 放大模板 当前模版为最小尺寸,每次循环放大2%,循环40次
for (int index = 0; index < largeResizeLoop; index++)
{
Mat tempTemplate = templatePic.Clone();
int newRows = (int)(templatePic.Rows + index * largeResize * templatePic.Rows);
int newCols = (int)(templatePic.Cols + index * largeResize * templatePic.Cols);
// resize模板图像大小
Cv2.Resize(tempTemplate, tempTemplate, new Size(newCols, newRows));
// 将得到的小模板进行与测试图像进行匹配
Mat matchResult = new Mat();
Cv2.MatchTemplate(testPic, tempTemplate, matchResult, TemplateMatchModes.CCoeffNormed);
// 解析模板匹配的结果 最大匹配区域以及匹配度
Cv2.MinMaxLoc(matchResult, out _, out tempConfidence, out _, out tempMaxLocal);
Rect tempRect = new Rect(tempMaxLocal, tempTemplate.Size());
// 判断当前匹配信任度 如果高于阈值 则结束匹配
if (earlyStop && tempConfidence > stopConfidence)
{
updateResult(index, tempMaxLocal, tempRect, tempConfidence);
saveResult(testPic, sourceImagePath, isSaveResult);
showPicture(testPic);
return;
}
// 记录当前最好的结果
if (tempConfidence > confidence)
{
updateResult(index, tempMaxLocal, tempRect, tempConfidence);
}
}
saveResult(testPic, sourceImagePath, isSaveResult);
showPicture(testPic);
return;
}
private void saveResult(Mat testPic, String sourceImagePath, bool isSaveResult)
{
if (!isSaveResult)
{
return;
}
Cv2.Rectangle(testPic, rect, Scalar.Green, 3);
Cv2.PutText(testPic, bestIndex.ToString() + ":" + Math.Round(confidence, 4).ToString(), new Point(maxLocal.X, maxLocal.Y - 50), HersheyFonts.HersheyDuplex, 2, Scalar.Red, 8);
Cv2.ImWrite(sourceImagePath.Split('.')[0] + "_matchResult.jpg", testPic);
}
private void showPicture(Mat testPic)
{
Cv2.NamedWindow("Result", WindowFlags.Normal);
Cv2.ImShow("Result", testPic);
Cv2.WaitKey(0);
}
/// <summary>
/// 更新最佳的匹配结果
/// </summary>
/// <param name="index">最佳序号</param>
/// <param name="tempMaxLocal">匹配点(左上角坐标)</param>
/// <param name="tempRect">、匹配框</param>
/// <param name="tempConfidence">匹配信任度</param>
private void updateResult(int index, Point tempMaxLocal, Rect tempRect, double tempConfidence)
{
bestIndex = index;
maxLocal = tempMaxLocal;
rect = tempRect;
confidence = tempConfidence;
return;
}
写得不是很优雅,只能说能运行起来吧!
看网上很多方法都是通过对比模板与匹配区域图像之间的方差和直方图之类的信息,来选择最终的结果,这是为什么呢?为啥不直接用匹配的分数作为选择依据呢?(以上代码就是依据匹配的分数来选择最好的匹配结果)