
简介模板匹配是计算机视觉中的基础技术用于在图像中定位与预设模板相似的区域。其核心原理是通过滑动窗口计算相似度但传统方法对目标的旋转和尺度变化敏感。为解决这一问题多角度多尺度匹配算法应运而生它通过构建模板金字塔和搜索图像金字塔在离散的角度和尺度空间中进行穷举搜索显著提升了算法的鲁棒性。该技术的工程价值在于平衡了精度与效率适用于对实时性有要求的场景。在工业质检领域零件常以任意角度出现在传送带上在机器人视觉导航中路标会因视角变化呈现不同尺度。本文介绍的LZC模板匹配算法正是基于OpenCV实现的一种增强方案它通过边缘特征提取和金字塔结构优化有效解决了传统方法在复杂场景下的局限性为自动化检测提供了可靠的视觉‘眼睛’。1. 项目概述从“找茬”到工业质检一个更聪明的“眼睛”在图像处理和计算机视觉的世界里“找东西”是一个永恒的基础课题。无论是让机器人从一堆零件里精准抓取目标还是在监控画面中锁定特定车辆亦或是在生产线上快速检测产品表面是否有瑕疵其核心都离不开一个关键步骤模板匹配。简单来说就是在一张大图我们称之为“搜索图像”里找到与预先准备好的一张小图“模板图像”最相似的位置。传统的模板匹配方法比如OpenCV里自带的cv::matchTemplate大家应该都用过。它速度快实现简单但局限性也相当明显模板和待搜索目标必须尺度一致、角度一致、光照条件接近。一旦目标发生了旋转、缩放或者因为视角变化产生了形变传统方法就很容易“抓瞎”匹配得分一落千丈导致漏检或误检。这就像你拿着身份证的正面标准照去人山人海里找一个侧着脸或者离你很远的人成功率可想而知。所以当我们需要在更复杂的现实场景中稳定、准确地“找东西”时一个多角度、多尺度的通用模板匹配算法就成了刚需。这就是“LZC模板匹配”算法要解决的核心问题。它不是一个学术界遥不可及的复杂模型而是一个立足于工程实践旨在提升OpenCV现有模板匹配鲁棒性的增强方案。我把它理解为给OpenCV的“眼睛”配上了一副“智能变焦和旋转稳定器”。这个算法特别适合那些对实时性有一定要求但又无法保证目标以固定姿态出现的场景。比如在自动化产线上零件可能以任意角度放置在传送带上在移动机器人视觉导航中路标可能因为机器人位姿变化而呈现不同尺度甚至在一些简单的游戏辅助或UI自动化测试中需要应对窗口缩放和控件旋转。接下来我将拆解如何用C和OpenCV一步步打造这样一个更聪明的“眼睛”。2. 核心思路拆解如何让模板“动”起来实现多角度多尺度匹配最直接的思路就是“穷举”既然不知道目标旋转了多少度、缩放了多少倍那我就把各种可能都试一遍。但这会带来两个核心挑战计算量爆炸和如何定义“相似度”。LZC算法的设计就是围绕解决这两个挑战展开的。2.1 算法流程总览整个算法的流程可以概括为一个“训练”加一个“搜索”的循环训练阶段离线/一次性的对输入的原始模板图像预先生成一个“模板金字塔”。这个金字塔不是简单的图像金字塔而是包含了该模板在多个离散旋转角度和缩放尺度下的所有可能形态。同时为金字塔中的每一层即每一个特定角度和尺度的模板提取一种鲁棒的特征描述子并存储起来。搜索阶段在线的对于每一帧待搜索的图像也构建其图像金字塔通常是尺度下采样。然后在每一个图像金字塔层级上使用训练阶段生成的所有模板变体进行滑动窗口匹配。匹配时不是直接比较像素而是比较之前提取的特征描述子计算一个相似度得分。最后在所有尺度、所有角度、所有位置上找到得分最高的位置即为最佳匹配。这个流程听起来简单但每一步都有大量细节和优化空间。关键在于我们如何高效地生成模板变体角度尺度以及采用何种特征来进行快速且鲁棒的相似度计算。2.2 为何选择“金字塔”结构“图像金字塔”是解决尺度问题的经典且高效的方法。其核心思想是通过不断下采样如每次缩小为原来的1/2生成一系列分辨率逐渐降低的图像。在高金字塔层级小图上我们可以快速进行粗定位因为搜索范围小在低金字塔层级大图上我们可以进行精确定位因为像素信息丰富。在LZC匹配中我们实际上构建了两个金字塔模板金字塔维度更多。它包含了(原始模板) × (N个尺度) × (M个角度)个模板图像。例如尺度集合为[0.8, 0.9, 1.0, 1.1, 1.2]角度集合为[-30° -15° 0° 15° 30°]那么对于一个原始模板就会生成5×525个不同的模板变体构成模板金字塔。搜索图像金字塔通常只进行尺度下采样用于加速搜索过程。在粗尺度上找到候选区域后可以映射到精细尺度上进行验证或更精确的定位。这种结构的优势在于它将连续的角度和尺度空间离散化将无限搜索问题转化为有限集合内的比较问题。只要离散化的步长设置合理角度步长如5°或10°尺度步长如0.9 1.0 1.1就能以可接受的计算成本覆盖较大的变化范围。注意尺度和角度的采样步长是精度和速度的权衡关键。步长越小覆盖越连续精度可能更高但模板金字塔规模呈平方增长计算量剧增。通常需要根据实际应用场景中目标可能的变化范围来设定。例如工业零件在传送带上的旋转通常不超过±180°我们可以以30°为步长采样而尺度变化可能仅在±20%以内。2.3 特征选择从像素到“签名”直接使用原始像素进行多角度多尺度匹配是灾难性的。旋转和缩放会严重改变像素的邻域关系。因此我们必须使用对几何变化具有一定不变性或鲁棒性的特征。在LZC算法的实践中以下几种特征是常见的选择边缘特征如Canny边缘物体的边缘轮廓对光照变化不敏感且对尺度和旋转变化具有一定的稳定性虽然轮廓会缩放和旋转但拓扑结构不变。将模板和搜索图都转换为二值边缘图后进行匹配是一种经典方法。OpenCV中的cv::Canny可以方便地提取边缘。方向梯度直方图HOG特征HOG通过计算和统计图像局部区域的梯度方向直方图来构成特征对几何和光学形变都有良好的不变性。虽然计算量相对边缘更大但描述能力更强。OpenCV提供了cv::HOGDescriptor用于计算HOG特征。局部二值模式LBP特征LBP对纹理的描述很好并且对单调的灰度变化具有不变性。计算速度很快。OpenCV的cv::LBP算法可以用于提取。特征点描述子如ORB这是一种更高级的思路。在模板上提取ORB等特征点和描述子然后在搜索图像上也提取再进行特征匹配如使用FLANN或暴力匹配。这种方法对视角变化、旋转、缩放都有很强的鲁棒性但更适合于具有丰富纹理的模板并且当目标被遮挡或纹理单一时会失效。在LZC算法的具体实现中为了平衡速度和鲁棒性边缘特征往往是首选的起点。它的计算极其快速二值化后的数据也便于进行快速的相似度度量如利用二值图像的相关性计算。我们可以将边缘图像作为我们的“特征图”进行后续的匹配操作。3. 核心模块实现详解有了理论框架我们开始用C和OpenCV将其实现。我将代码结构分为几个核心类方便理解和复用。3.1 数据结构定义TemplatePyramid首先我们需要一个数据结构来管理训练阶段生成的所有模板变体。// TemplateStructure.h #ifndef TEMPLATE_STRUCTURE_H #define TEMPLATE_STRUCTURE_H #include opencv2/opencv.hpp #include vector struct SingleTemplate { cv::Mat image; // 该角度和尺度下的模板图像通常是特征图如边缘图 cv::Mat mask; // 可选的掩膜用于指定模板中哪些区域参与匹配 double angle; // 旋转角度度 double scale; // 缩放系数 cv::Point2f center; // 模板中心点在原始坐标系中用于后续结果映射 // 可以扩展存储其他预计算的特征如HOG描述子向量 }; class TemplatePyramid { public: TemplatePyramid() default; // 根据原始模板、角度列表、尺度列表生成金字塔 bool build(const cv::Mat srcTemplate, const std::vectordouble scales, const std::vectordouble angles, int cannyLowThresh 50, int cannyHighThresh 150); // 边缘检测参数 // 获取金字塔中所有模板 const std::vectorSingleTemplate getAllTemplates() const { return pyramid_; } // 根据索引获取特定模板 const SingleTemplate getTemplate(size_t index) const { return pyramid_.at(index); } size_t size() const { return pyramid_.size(); } private: std::vectorSingleTemplate pyramid_; cv::Size originalSize_; // 原始模板尺寸 // 内部方法对图像进行旋转和缩放并提取特征这里以边缘为例 cv::Mat preprocessTemplate(const cv::Mat src, double angle, double scale, int cannyLowThresh, int cannyHighThresh, cv::Point2f outputCenter); }; #endif // TEMPLATE_STRUCTURE_Hbuild函数是这个类的核心。其内部逻辑如下遍历传入的scales和angles向量进行两层循环。对于每一对(scale, angle)调用preprocessTemplate函数。preprocessTemplate函数根据scale计算新的尺寸newSize originalSize * scale。使用cv::resize进行缩放。根据angle计算旋转矩阵使用cv::getRotationMatrix2D围绕图像中心旋转并用cv::warpAffine进行旋转。这里一个关键细节是旋转后图像尺寸会变大需要计算新的边界框或者统一放置在一个足够大的画布上以确保信息不丢失。对旋转缩放后的图像使用cv::Canny提取边缘得到二值特征图。记录下此时模板中心点相对于原始图像坐标系的变换后位置存入SingleTemplate的center字段。这个点至关重要它是在搜索图像中找到匹配位置后将匹配框映射回原始视角的关键。3.2 匹配引擎MultiScaleAngleMatcher这个类负责在搜索图像中使用构建好的模板金字塔进行滑动窗口匹配。// MatcherEngine.h #ifndef MATCHER_ENGINE_H #define MATCHER_ENGINE_H #include TemplateStructure.h #include opencv2/opencv.hpp struct MatchResult { cv::Rect location; // 在搜索图像上的匹配矩形框 double confidence; // 匹配置信度 [0, 1] double matchedAngle; // 匹配到的角度 double matchedScale; // 匹配到的尺度 int pyramidLevel; // 在搜索图像金字塔的哪一层找到的 }; class MultiScaleAngleMatcher { public: MultiScaleAngleMatcher(const TemplatePyramid tmplPyramid); // 主匹配函数 MatchResult match(const cv::Mat searchImage, double scaleStep 0.9, // 搜索图像金字塔的尺度步长 int maxPyramidLevels 4, // 最大金字塔层数 cv::TemplateMatchModes method cv::TM_CCOEFF_NORMED); // 批量匹配返回前N个最佳结果 std::vectorMatchResult matchMultiple(const cv::Mat searchImage, int topK, double scaleStep 0.9, int maxPyramidLevels 4, cv::TemplateMatchModes method cv::TM_CCOEFF_NORMED, double minScore 0.7); // 最低置信度阈值 private: const TemplatePyramid tmplPyramid_; // 引用已构建的模板金字塔 // 在单层搜索图像上匹配单个模板 double matchSingleTemplateOnLevel(const cv::Mat searchLevel, const SingleTemplate tmpl, cv::Point maxLoc, cv::TemplateMatchModes method); // 非极大值抑制用于过滤重叠的匹配结果 std::vectorMatchResult nonMaximumSuppression(const std::vectorMatchResult candidates, double overlapThreshold 0.5); }; #endif // MATCHER_ENGINE_Hmatch函数的实现是算法的核心搜索循环构建搜索图像金字塔根据scaleStep和maxPyramidLevels对输入的searchImage进行逐层下采样得到一系列不同尺度的搜索图。最顶层level 0是原图level 1是原图*scaleStep 以此类推。多层循环匹配外层循环遍历搜索图像金字塔的每一层searchLevel。内层循环遍历模板金字塔tmplPyramid_中的每一个SingleTemplate。核心调用对于每一对(searchLevel, tmpl)使用OpenCV的cv::matchTemplate函数进行计算。这里有一个重要技巧我们的tmpl.image已经是边缘特征图而searchLevel也需要预先转换为边缘图。但为了效率我们可以在每一层searchLevel上只做一次Canny边缘检测然后与所有模板进行匹配。匹配方法method通常选择归一化的相关系数法cv::TM_CCOEFF_NORMED它对光照变化有一定鲁棒性且输出得分在[-1,1]之间1表示完美匹配。结果聚合与映射在每一次cv::matchTemplate后会得到一个响应图response map。通过cv::minMaxLoc找到响应图中的最大值及其位置maxLoc这个位置就是当前模板在当前搜索层上的最佳匹配位置。由于搜索图像是下采样的找到的maxLoc坐标需要根据当前金字塔层级映射回原始搜索图像的坐标系。同时匹配到的矩形框尺寸也需要根据当前层级的缩放系数进行放大。更重要的是我们匹配到的矩形框是基于旋转缩放后的模板的。为了在原始搜索图像上画出一个方向正确的矩形我们需要利用模板的angle和center信息对矩形进行逆向的旋转和平移变换。这涉及到一些基本的几何计算。选择最佳匹配在所有层级、所有模板的匹配结果中保留置信度confidence最高的一个将其角度、尺度、位置信息封装成MatchResult返回。matchMultiple函数逻辑类似但会维护一个候选结果列表最后通过非极大值抑制NMS来剔除那些位置高度重叠但置信度较低的结果返回前topK个不同的匹配目标。这在有多实例的场景下非常有用。3.3 几何变换与结果可视化这是最容易出错的部分。当我们得到一个在旋转缩放后的模板坐标系下的匹配点maxLoc时需要将其转换回原始搜索图像中一个带角度的矩形。// GeometryUtils.cpp (部分关键代码) #include opencv2/opencv.hpp #include cmath /** * brief 根据匹配结果计算原始搜索图像中带旋转的矩形 * param matchLoc 在搜索图像某金字塔层上的匹配点左上角 * param tmplSize 匹配所用模板的尺寸 * param matchedScale 匹配到的尺度搜索图金字塔层的尺度倒数 * param matchedAngle 匹配到的角度 * param tmplCenter 模板中心在原始模板坐标系中的位置经过预处理的 * return 在原始搜索图像坐标系中的旋转矩形 */ cv::RotatedRect calculateResultRoi(const cv::Point matchLoc, const cv::Size tmplSize, double matchedScale, double matchedAngle, const cv::Point2f tmplCenter) { // 1. 将匹配点坐标映射回原始搜索图像尺度 cv::Point2f locInOriginal(matchLoc.x / matchedScale, matchLoc.y / matchedScale); // 2. 模板的原始中心点在模板图像坐标系中经过匹配位置偏移后得到在搜索图像中的对应中心点 // 注意matchLoc是模板左上角在搜索图中的位置。 // 模板中心在搜索图中的位置 matchLoc tmplCenter cv::Point2f templateCenterInSearch(matchLoc.x tmplCenter.x, matchLoc.y tmplCenter.y); cv::Point2f centerInOriginal(templateCenterInSearch.x / matchedScale, templateCenterInSearch.y / matchedScale); // 3. 计算矩形在原始图像中的尺寸模板尺寸 * 匹配到的尺度这里需要仔细 // 实际上模板金字塔中的模板已经是经过缩放和旋转的。 // matchedScale 是搜索图金字塔层的缩放系数用于坐标映射。 // 矩形的大小就是当前模板tmplSize映射回原始搜索图的大小tmplSize / matchedScale cv::Size2f sizeInOriginal(static_castfloat(tmplSize.width) / matchedScale, static_castfloat(tmplSize.height) / matchedScale); // 4. 角度匹配到的角度就是矩形的旋转角度。注意OpenCV中RotatedRect的角度定义。 // 通常cv::warpAffine正向旋转图像那么逆向旋转矩形就应该用负角度。 // 需要与预处理阶段旋转模板的方式保持一致。 double rectAngle -matchedAngle; // 假设预处理是正向旋转模板 return cv::RotatedRect(centerInOriginal, sizeInOriginal, rectAngle); }可视化时使用cv::RotatedRect的points方法获取四个顶点然后用cv::line或cv::polylines画出来。4. 性能优化与工程实践要点一个可用的算法和一个高效的算法之间隔着巨大的优化空间。以下是几个关键的优化方向和实操心得。4.1 加速策略让搜索快起来搜索空间剪枝尺度优先粗定位先在搜索图像金字塔的顶层最小图进行全角度匹配。因为图像小计算极快。得到粗略的位置和尺度后在下层金字塔中可以只在一个较小的感兴趣区域ROI内进行精细搜索和角度微调而不是全图搜索。角度连续性假设在视频流或连续帧中目标的角度通常不会发生突变。可以利用前一帧匹配到的角度在当前帧只搜索该角度附近的一个较小范围如±10°而不是全角度范围。利用图像金字塔的级联拒绝在高层金字塔低分辨率匹配时设定一个较高的置信度阈值。只有那些在粗尺度上得分就很高的位置才有资格进入下一层精细尺度的搜索。这样可以过滤掉大量的错误区域。并行计算这是最直接的加速手段。模板金字塔中的各个模板之间是相互独立的可以完美并行。使用C11的std::async或OpenMP指令可以轻松地将内层循环遍历模板并行化。#pragma omp parallel for collapse(2) // 两层循环并行 for (int l 0; l searchPyramid.size(); l) { for (int t 0; t tmplPyramid_.size(); t) { // ... 匹配计算将结果写入线程安全的容器 } }注意并行化时对共享数据如结果容器的写入需要加锁或使用原子操作避免竞争条件。特征计算优化边缘检测Canny是相对耗时的操作。可以复用特征图搜索图像每层的边缘图只需计算一次模板的边缘图在训练阶段就已计算好。4.2 参数调优指南算法有一系列参数需要根据实际场景调整参数含义调优建议影响角度范围与步长angles: e.g.,{-30, -15, 0, 15, 30}根据目标旋转自由度设定。步长越小精度越高计算越慢。通常10°~15°起步。直接决定模板金字塔大小和匹配精度。尺度范围与步长scales: e.g.,{0.8, 0.9, 1.0, 1.1, 1.2}根据目标尺度变化范围设定。步长常用等比系数如0.9。影响尺度搜索的覆盖率和计算量。Canny阈值cannyLowThresh,cannyHighThresh低阈值与高阈值之比通常在2:1到3:1之间。需要根据图像对比度调整确保能提取出完整、连贯的目标边缘。影响特征图的质量。阈值过高会丢失边缘过低会引入噪声。匹配方法cv::TemplateMatchModesTM_CCOEFF_NORMED归一化相关系数最常用抗光照干扰。TM_SQDIFF_NORMED归一化平方差也可考虑。影响相似度度量的鲁棒性。置信度阈值minScore通过实验确定。在测试集上观察正确匹配和错误匹配的得分分布取一个折中的值如0.75。低于此阈值的结果将被过滤影响召回率和准确率。NMS重叠阈值overlapThreshold通常设为0.3~0.5。用于matchMultiple时剔除重叠框。影响多目标检测时对邻近目标的区分能力。调优流程建议收集代表性数据包含各种角度、尺度、光照、背景干扰的目标图像。固定其他参数单参数扫描例如先固定一个较大的角度步长如30°和尺度步长调整Canny阈值确保边缘提取良好。评估指标不仅要看匹配成功率还要看单帧处理耗时。在准确率和速度之间找到业务可接受的平衡点。录制日志将每次匹配的最佳得分、角度、尺度输出到日志文件分析在哪些情况下算法会失效。4.3 集成与部署注意事项内存管理模板金字塔会占用可观的内存尤其是角度和尺度采样很密的时候。对于嵌入式或内存受限的平台需要权衡。可以考虑动态加载或使用更紧凑的特征表示如将二值边缘图转换为轮廓点集存储。实时性保障在视频处理中如果单帧处理时间超过帧间隔如33ms for 30fps会导致延迟累积。必须进行严格的性能剖析Profiling使用工具如gprof或Valgrind找出热点函数。通常cv::matchTemplate和cv::Canny是主要耗时部分。与OpenCV版本兼容确保使用的OpenCV函数在不同版本间API一致。例如某些版本中cv::RotatedRect的angle定义可能不同。错误处理增加健壮性检查。例如输入图像是否为空、模板金字塔是否已构建、匹配得分是否有效非NaN等。5. 常见问题排查与实战技巧在实际开发和调试中你会遇到各种各样的问题。下面是我踩过的一些坑和解决方法。5.1 匹配结果框位置或角度不准症状算法找到了目标但画出来的矩形框要么没套住目标要么角度歪了。排查思路检查坐标变换链这是最常见的原因。从模板中心点tmplCenter到匹配点matchLoc再到最终RotatedRect的中心和角度每一步的坐标系变换特别是缩放因子都必须清晰无误。建议单独写一个测试函数用一张纯色图和一个简单的矩形模板进行0度、0缩放的匹配看框是否能严丝合缝。然后逐步增加旋转和缩放验证变换的正确性。验证模板中心点在preprocessTemplate函数中旋转缩放后模板图像的尺寸和内容中心可能发生了变化。确保tmplCenter计算的是内容区域的中心而不是画布的中心如果旋转后画布有留黑边。有时需要先找到二值边缘图的最小外接矩形以其中心作为有效内容的中心。检查OpenCV的旋转方向OpenCV中图像旋转的角度与RotatedRect中角度的定义可能方向相反。记住一个原则用同样的角度参数去旋转一个水平矩形应该能得到和匹配模板一样的姿态。如果方向反了在calculateResultRoi中对matchedAngle取负即可。5.2 算法速度太慢无法满足实时要求症状处理一帧图像需要几百毫秒甚至几秒。排查与优化使用性能分析工具这是第一步。不要靠猜。用std::chrono高精度时钟分别测量build模板金字塔、为搜索图构建金字塔、以及最内层匹配循环的时间。你会发现瓶颈所在。减少金字塔层级和采样密度这是最有效的办法。问自己业务真的需要360度全角度搜索吗尺度变化范围真的有那么大吗通常将角度步长从10°增加到15°尺度数量从5个减少到3个速度能提升数倍而精度损失可能在接受范围内。缩小搜索区域如果目标在图像中的大致位置是已知的比如总是在画面中央区域就不要在全图搜索。先用一个检测器或上一帧的结果确定一个ROI只在ROI内进行精细匹配。启用编译优化确保在Release模式下编译并开启编译器优化选项如GCC的-O2或-O3 MSVC的/O2。检查是否意外复制了大矩阵在C中cv::Mat的赋值是浅拷贝。但如果你在函数参数中使用了cv::Mat的值传递或者不小心调用了cv::Mat::clone()会导致不必要的数据复制。尽量使用const cv::Mat传递引用。5.3 在复杂背景或低对比度下匹配失败症状目标明明在但匹配得分很低或者错误地匹配到了背景上类似的图案。解决方案优化预处理尝试在Canny边缘检测前对图像进行预处理。例如高斯模糊cv::GaussianBlur可以平滑噪声让边缘更干净。直方图均衡化cv::equalizeHist可以增强对比度特别是在光照不均的情况下。注意它对全局进行操作有时会过度增强噪声。可以考虑使用限制对比度的自适应直方图均衡化CLAHE效果更好。使用掩膜Mask在构建模板时可以提供一个掩膜指定只有模板的特定区域如目标主体参与匹配。这能有效排除背景干扰。在cv::matchTemplate函数中有些匹配方法是支持掩膜参数的。尝试更强的特征如果边缘特征太弱或太杂乱可以考虑切换到HOG特征。HOG对形状的描述更全局化抗噪声能力更强。虽然计算更慢但匹配精度可能会显著提升。你可以实现一个“特征选择开关”在初始化TemplatePyramid时决定使用边缘还是HOG。后处理与融合不要完全依赖单次匹配得分。可以结合其他线索例如匹配到的区域颜色直方图是否与模板相似或者在视频中目标的位置和运动是否有连续性可以引入简单的卡尔曼滤波进行轨迹预测将预测位置作为先验辅助匹配。5.4 多目标检测时互相干扰症状当图像中存在多个相同或相似目标时算法只返回一个或者返回的框都集中在同一个目标上。解决方案确保使用matchMultiple和NMS这是基础。matchSingleTemplateOnLevel返回的响应图通常只有一个全局极值点。你需要遍历模板金字塔的所有层和所有模板收集所有超过阈值的候选结果然后用NMS来筛选。调整NMS阈值如果两个目标靠得很近标准的NMS基于IoU可能会把其中一个抑制掉。可以适当降低overlapThreshold比如从0.5降到0.3或者使用更宽松的抑制准则如只抑制得分低且重叠度非常高的框。分区域搜索在一个目标被成功匹配并标记后可以将该区域从搜索图像中“抹除”例如用背景色填充匹配框区域然后在修改后的图像中继续搜索下一个目标。这种方法简单粗暴但要注意“抹除”可能破坏邻近目标的边缘。经过以上这些步骤一个鲁棒、实用的多角度多尺度模板匹配算法就搭建起来了。它绝不是万能的在极端光照、严重遮挡、非刚性形变的情况下仍然会失效。但对于许多结构化的工业视觉场景、辅助定位任务来说它提供了一个在传统方法和深度学习之间很好的平衡点不需要训练数据、可解释性强、速度可控并且足够有效。本文还有配套的精品资源点击获取