目标
在本教程中,您将学习如何:
- 使用 OpenCV 函数 matchTemplate() 搜索图像补丁和输入图像之间的匹配项
- 使用 OpenCV 函数 minMaxLoc() 查找给定数组中的最大值和最小值(以及它们的位置)。
理论
什么是模板匹配?
模板匹配是一种用于查找图像中与模板图像(补丁)匹配(相似)的区域的技术。
虽然补丁必须是矩形,但并非所有矩形都是相关的。在这种情况下,可以使用掩码来隔离补丁中用于查找匹配项的部分。
它是如何工作的?
-
我们需要两个主要组件:
- **源图片(I):**我们希望在其中找到与模板图像匹配的图像
- **模板图片 (T):**将与源映像进行比较的修补程序映像
我们的目标是检测最高匹配区域:
-
为了识别匹配区域,我们必须通过滑动模板图像与源图像进行比较:
-
滑动是指一次移动一个像素(从左到右,从上到下)。在每个位置,都会计算一个指标,以便它表示该位置的匹配度的“好”或“坏”程度(或补丁与源图像的特定区域的相似程度)。
-
对于 T over I 的每个位置,将指标存储在结果矩阵 R 中。R 中的每个位置 都包含匹配指标:(x,y)
上图是用公制TM_CCORR_NORMED滑动补丁的结果 R。最亮的位置表示匹配度最高。如您所见,红色圆圈标记的位置可能是具有最高值的位置,因此该位置(由该点形成的矩形作为角,宽度和高度等于贴片图像)被视为匹配项。
-
在实践中,我们使用函数 minMaxLoc() 在 R 矩阵中定位最高值(或更低,取决于匹配方法的类型)
面膜是如何工作的?
-
如果匹配需要遮罩,则需要三个组件:
- **源图片(I):**我们希望在其中找到与模板图像匹配的图像
- **模板图片 (T):**将与源映像进行比较的修补程序映像
- **蒙版图像 (M):**蒙版,遮罩模板的灰度图像
-
目前只有两种匹配方法接受掩码:TM_SQDIFF 和 TM_CCORR_NORMED(有关 opencv 中可用的所有匹配方法的说明,请参见下文)。
-
蒙版的尺寸必须与模板相同
-
蒙版应具有CV_8U或CV_32F深度,并且通道数应与模板图像相同。CV_8U情况下,掩码值被视为二进制值,即零和非零。CV_32F情况下,这些值应落在 [0…1] 范围内,模板像素将乘以相应的掩码像素值。由于示例中的输入图像具有CV_8UC3类型,因此蒙版也被视为彩色图像。
OpenCV中有哪些匹配方法?
问得好。OpenCV 在函数 matchTemplate() 中实现了模板匹配。可用的方法有 6 种:
法典 C++爪哇岛蟒
-
这个程序是做什么的?
- 加载输入图像、图像修补程序(模板)和(可选)蒙版
- 通过使用 OpenCV 函数 matchTemplate() 和前面描述的 6 种匹配方法中的任何一种来执行模板匹配过程。用户可以通过在跟踪栏中输入选择来选择方法。如果提供了掩码,则它只会用于支持掩码的方法
- 规范化匹配过程的输出
- 以更高的匹配概率定位位置
- 在与最高匹配对应的区域周围绘制一个矩形
-
可下载代码:点击这里
-
代码一览:
#include“opencv2/imgcodecs.hpp”
#include “opencv2/highgui.hpp”
#include“opencv2/imgproc.hpp”
#include < iostream>
使用命名空间 std;
使用命名空间 CV;
布尔use_mask;
const char* image_window = “源图像”;
const char* result_window = “结果窗口”;
int match_method;
整数 max_Trackbar = 5;
无效MatchingMethod( int, void* );
const char* 键 =
“{ 帮助 h| |打印帮助消息。}"
“{ @input1 |Template_Matching_Original_Image.jpg |image_name}”
“{ @input2 |Template_Matching_Template_Image.jpg |template_name }”
“{ @input3 | |mask_name }”;
int main( int argc, char** argv )
{
CommandLineParser 解析器( argc, argv, keys );
samples::addSamplesDataSearchSubDirectory( “doc/tutorials/imgproc/histograms/template_matching/images” );
img = imread( samples::findFile( parser.get<String>(“@input1”) ) );
templ = imread( samples::findFile( parser.get<String>(“@input2”) ), IMREAD_COLOR );
if(argc > 3) {
use_mask = 真;
mask = imread(samples::findFile( parser.get[>(“@input3”) ), IMREAD_COLOR );
}
if(img.empty() ||模板。empty() ||(use_mask & 掩码。空()))
{
cout <<“无法阅读其中一张图像”<<结束;
返回EXIT_FAILURE;
}
namedWindow( image_window, WINDOW_AUTOSIZE );
namedWindow( result_window, WINDOW_AUTOSIZE );
const char* trackbar_label = “方法: \n 0: SQDIFF \n 1: SQDIFF 规范 \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF 规范”;
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
Matching方法( 0, 0 );
waitKey(0);
返回EXIT_SUCCESS;
}
无效MatchingMethod( int, void* )
{
垫子img_display;
图片。copyTo( img_display );
int result_cols = img。科尔斯 - 模板。科尔斯 + 1;
int result_rows = img。行 - 模板。行数 + 1;
结果。create( result_rows, result_cols, CV_32FC1 );
bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
如果 (use_mask & method_accepts_mask)
{ matchTemplate( img, templ, result, match_method, mask);}
还
{ matchTemplate( img, templ, result, match_method);}
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
双minVal;双倍最大Val;点 minLoc;点 maxLoc;
点匹配 Loc;
minMaxLoc( 结果, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
{ matchLoc = minLoc;
还
{ matchLoc = 最大 Loc;
rectangle( img_display, matchLoc, Point( matchLoc.x + 模板。cols , matchLoc.y + 模板。rows ), Scalar::all(0), 2, 8, 0 );
rectangle( result, matchLoc, Point( matchLoc.x + 模板。cols , matchLoc.y + 模板。rows ), Scalar::all(0), 2, 8, 0 );
imshow( image_window, img_display );
imshow( result_window, 结果 );
返回;
}
解释 C++爪哇岛蟒
- 声明一些全局变量,例如图像、模板和结果矩阵,以及匹配方法和窗口名称:
布尔use_mask;
垫子img;垫子模板;垫面膜;垫子结果;
const char* image_window = “源图像”;
const char* result_window = “结果窗口”;
int match_method;
整数 max_Trackbar = 5;
- 加载源图像、模板,以及(如果匹配方法支持)蒙版(可选):
img = imread( samples::findFile( parser.get<String>(“@input1”) ) );
templ = imread( samples::findFile( parser.get<String>(“@input2”) ), IMREAD_COLOR );
if(argc > 3) {
use_mask = 真;
mask = imread(samples::findFile( parser.get[>(“@input3”) ), IMREAD_COLOR );
}
if(img.empty() || templ.empty() ||(use_mask && mask.empty()))
{
cout <<“无法阅读其中一张图像”<<结束;
返回EXIT_FAILURE;
}
-
创建跟踪栏以输入要使用的匹配方法类型。当检测到更改时,将调用回调函数。
const char* trackbar_label = “方法: \n 0: SQDIFF \n 1: SQDIFF 规范 \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF 规范”;
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
-
让我们来看看回调函数。首先,它创建源图像的副本:
垫子img_display;
img.copyTo( img_display );
-
执行模板匹配操作。参数自然是输入图像 I、模板 T、结果 R 和 match_method(由 Trackbar 给出),以及可选的掩码图像 M。
bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
如果 (use_mask & method_accepts_mask)
{ matchTemplate( img, templ, result, match_method, mask);}
还
{ matchTemplate( img, templ, result, match_method);}
-
我们对结果进行归一化:
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
-
我们使用 minMaxLoc() 在结果矩阵 R 中定位最小值和最大值。
双minVal;双倍最大Val;点 minLoc;点 maxLoc;
点匹配 Loc;
minMaxLoc( 结果, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
-
对于前两种方法(TM_SQDIFF 和 MT_SQDIFF_NORMED ),最佳匹配是最低值。对于所有其他值,值越高表示匹配越好。因此,我们将相应的值保存在 matchLoc 变量中:
if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
{ matchLoc = minLoc;
还
{ matchLoc = 最大 Loc;
-
显示源图像和结果矩阵。在尽可能高的匹配区域周围绘制一个矩形:
rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
imshow( image_window, img_display );
imshow( result_window, 结果 );
结果
-
使用输入图像测试我们的程序,例如:
和模板图像:
-
生成以下结果矩阵(第一行是标准方法 SQDIFF、CCORR 和 CCOEFF,第二行是规范化版本中的相同方法)。在第一列中,最暗是更好的匹配,对于其他两列,位置越亮,匹配度越高。
Result_0
Result_1
Result_2
Result_3
Result_4
Result_5
-
右边的匹配如下所示(右边的人脸周围的黑色矩形)。请注意,CCORR 和 CCDEFF 给出了错误的最佳匹配,但是它们的规范化版本做对了,这可能是因为我们只考虑了“最高匹配”,而不是其他可能的高匹配。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
😝有需要的小伙伴,可以Vx扫描下方二维码免费领取==🆓