基于形状的模板匹配,多模板匹配,支持旋转、缩放、亚像素位置提取,动态链接库,C++/C#都可使用,
在计算机视觉领域,基于形状的模板匹配是一项非常重要的技术,它在目标识别、图像检测等诸多场景都有广泛应用。今天咱就来深入聊聊这其中涉及到支持旋转、缩放、亚像素位置提取,还能通过动态链接库让C++ 与C# 都能使用的多模板匹配技术。
基于形状的模板匹配基础
基于形状的模板匹配,简单来说就是在一幅图像中寻找与给定模板形状相似的目标。传统的模板匹配可能只是单纯比对像素值,但基于形状的匹配更侧重于目标的轮廓和几何特征,这使得它对光照变化、噪声等干扰有更好的鲁棒性。
多模板匹配
实际应用中,我们常常需要在一幅图像里查找多个不同的目标,这就用到多模板匹配。比如在一个复杂的工业零件图像中,要同时找出不同型号的螺丝、螺母等零件。
假设我们用OpenCV库来实现多模板匹配,在C++ 里代码大概像这样:
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { Mat image = imread("input_image.jpg", IMREAD_GRAYSCALE); Mat template1 = imread("template1.jpg", IMREAD_GRAYSCALE); Mat template2 = imread("template2.jpg", IMREAD_GRAYSCALE); if (image.empty() || template1.empty() || template2.empty()) { cout << "Could not open or find the images" << endl; return -1; } Mat result1, result2; int match_method = TM_CCOEFF_NORMED; matchTemplate(image, template1, result1, match_method); matchTemplate(image, template2, result2, match_method); normalize(result1, result1, 0, 1, NORM_MINMAX, -1, Mat()); normalize(result2, result2, 0, 1, NORM_MINMAX, -1, Mat()); double minVal1, maxVal1; Point minLoc1, maxLoc1; minMaxLoc(result1, &minVal1, &maxVal1, &minLoc1, &maxLoc1, Mat()); double minVal2, maxVal2; Point minLoc2, maxLoc2; minMaxLoc(result2, &minVal2, &maxVal2, &minLoc2, &maxLoc2, Mat()); rectangle(image, maxLoc1, Point(maxLoc1.x + template1.cols, maxLoc1.y + template1.rows), Scalar(0, 255, 0), 2, 8, 0); rectangle(image, maxLoc2, Point(maxLoc2.x + template2.cols, maxLoc2.y + template2.rows), Scalar(0, 255, 0), 2, 8, 0); imshow("Matched Image", image); waitKey(0); return 0; }这段代码首先读取输入图像和两个模板图像,然后使用matchTemplate函数分别对两个模板在图像上进行匹配,再通过normalize函数对匹配结果进行归一化处理,最后找出匹配度最高的位置并在原图上画出矩形框标记出目标位置。
支持旋转、缩放
实际场景中的目标可能存在旋转和缩放变化,这就需要更复杂的处理。我们可以使用一些特征描述子,比如SIFT(尺度不变特征变换)或SURF(加速稳健特征)。
以SIFT为例,C++ 代码如下:
#include <opencv2/opencv.hpp> #include <opencv2/xfeatures2d.hpp> using namespace cv; using namespace cv::xfeatures2d; int main() { Mat image = imread("input_image.jpg", IMREAD_GRAYSCALE); Mat templateImage = imread("template_image.jpg", IMREAD_GRAYSCALE); Ptr<SIFT> detector = SIFT::create(); std::vector<KeyPoint> keypoints1, keypoints2; Mat descriptors1, descriptors2; detector->detectAndCompute(image, Mat(), keypoints1, descriptors1); detector->detectAndCompute(templateImage, Mat(), keypoints2, descriptors2); Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); std::vector<std::vector<DMatch>> knnMatches; matcher->knnMatch(descriptors1, descriptors2, knnMatches, 2); std::vector<DMatch> goodMatches; for (size_t i = 0; i < knnMatches.size(); i++) { if (knnMatches[i][0].distance < 0.7 * knnMatches[i][1].distance) { goodMatches.push_back(knnMatches[i][0]); } } Mat imgMatches; drawMatches(image, keypoints1, templateImage, keypoints2, goodMatches, imgMatches, Scalar::all(-1), Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); imshow("Matches", imgMatches); waitKey(0); return 0; }这里代码利用SIFT检测器提取图像和模板的关键点与描述子,通过FLANN匹配器进行匹配,并筛选出较好的匹配点,最后画出匹配结果。SIFT的特性使得它能够在一定程度上应对目标的旋转和缩放变化。
亚像素位置提取
有时候我们需要更精确地定位目标,亚像素位置提取就派上用场了。OpenCV中可以利用cornerSubPix函数来实现。
// 假设已经找到关键点keypoints Mat image = imread("input_image.jpg", IMREAD_GRAYSCALE); TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001); cornerSubPix(image, keypoints, Size(5, 5), Size(-1, -1), criteria);这段代码对已有的关键点进行亚像素级别的优化,通过设置终止条件,不断迭代直到满足精度要求,从而得到更精确的关键点位置。
动态链接库实现C++/C# 通用
为了让C++ 和C# 都能使用上述功能,我们可以创建动态链接库(DLL)。在C++ 中创建DLL,比如定义一个函数用于模板匹配:
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "stdafx.h" #include <opencv2/opencv.hpp> extern "C" __declspec(dllexport) void MatchTemplateInDLL(const char* imagePath, const char* templatePath) { Mat image = imread(imagePath, IMREAD_GRAYSCALE); Mat templateImage = imread(templatePath, IMREAD_GRAYSCALE); Mat result; int match_method = TM_CCOEFF_NORMED; matchTemplate(image, templateImage, result, match_method); normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat()); double minVal, maxVal; Point minLoc, maxLoc; minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat()); rectangle(image, maxLoc, Point(maxLoc.x + templateImage.cols, maxLoc.y + templateImage.rows), Scalar(0, 255, 0), 2, 8, 0); imshow("Matched in DLL", image); waitKey(0); }在C# 中调用这个DLL函数:
using System; using System.Runtime.InteropServices; class Program { [DllImport("YourDLLName.dll", EntryPoint = "MatchTemplateInDLL", CharSet = CharSet.Ansi)] public static extern void MatchTemplateInDLL(string imagePath, string templatePath); static void Main() { string imagePath = "input_image.jpg"; string templatePath = "template_image.jpg"; MatchTemplateInDLL(imagePath, templatePath); } }这样通过动态链接库,实现了C++ 与C# 对基于形状模板匹配功能的通用调用。
总之,基于形状的多模板匹配,结合旋转、缩放、亚像素位置提取以及动态链接库的使用,为计算机视觉应用提供了强大而灵活的工具,在工业检测、安防监控等众多领域都有着广阔的应用前景。