发布时间:2023-09-27 15:30
所以今天就趁机会讲讲模板匹配,正好之前的项目有一部分重要工作就是和模板匹配紧密相关,对于今天作业来说,之前的项目难度更大,因为涉及到许多要考虑的因素,还要考虑效率实时性等问题。太详细的我也不方便展开,下面先看看之前的效果
17_50_13_9
当然也有其他的车型,视频就不放了直接上结果图
现在就正式开始解决今天这个作业
在上面视频拼车里模板都是在每一帧中取得,并且为了模板匹配能匹配得上还要预处理每一帧图片,调整模板区域、大小以及匹配方法。但是在拼车中我们只需要匹配一次,而作业中明显是需要多目标匹配的,并且待匹配目标的角度差异很大,在这种情况下使用opencv中的matchTemplate()
效果肯定很差,至于为什么这可以从原理上解释。
有兴趣的可以去看看相关资料,我就根据上图简单来说说模板匹配流程
平方差方法匹配值越小越好,相关性方法匹配值越大越好,这样就会得到一个result矩阵,其中有最亮/最暗的地方就是我们匹配到的地方。利用minMaxLoc()
找到这些地方,那也就代表找到了待匹配目标。
但是,我在上面也说过了,这些待匹配目标和模板的角度差异特别大,你用模板匹配最多也只能匹配出和模板一样大小、方向的待匹配目标。或者利用一堆复杂的操作,把模板图片进行角度变换搞一个任意角度都有的模板组然后再一一匹配,这个倒是可行,但是效率上不敢说。所以干脆换一种思路,不用模板匹配而是用形状去匹配。
匹配嘛就是根据相似性进行比较,上面已经说了因为角度导致模板匹配失效,那就得想办法排除掉角度干扰,最容易想到的就是利用某些角度不变特性的算子,所以很自然就想到HuMoments,关于Hu不变矩可以看看opencv的解释
利用归一化中心矩构造了不变特征矩,这个对于平移,旋转都可以保持不变性,这不就是我们需要的吗?然后利用这个开始实现作业。
这是opencv中形状匹配的函数以及参数,我之前也没用过所以放在这一起学习一下。
我们只有原图,并没有模板图。自己想去截图但是待匹配图之间挨得比较近,截下来的模板图不太容易找轮廓,因此直接在待匹配图操作,找到其中一个的轮廓作为模板。
vector TemplateContours(Mat img_template)
{
//灰度化
Mat gray_img_template;
cvtColor(img_template, gray_img_template, COLOR_BGR2GRAY);
//二值化
Mat thresh_img_template;
threshold(gray_img_template, thresh_img_template, 0, 255, THRESH_OTSU);
imshow(\"b\", thresh_img_template);
//膨胀处理
Mat ellipse = getStructuringElement(MORPH_ELLIPSE, Size(15, 15));
morphologyEx(thresh_img_template, thresh_img_template, MORPH_OPEN, ellipse, Point(-1, -1), 1);
//寻找边界
vector> contours_template;
vector hierarchy;
findContours(thresh_img_template, contours_template, hierarchy,RETR_LIST, CHAIN_APPROX_SIMPLE, Point());
//绘制边界,查看是否是我们的目标轮廓
drawContours(img_template, contours_template, 0, Scalar(0, 0, 255), 1, 8, hierarchy);
imshow(\"img_template\", img_template);
waitKey(0);
return contours_template[0];
}
那个红色的圈就是每个圆形垫子的共有特征,然后根据这个圈作为模板轮廓对图像进行形状匹配,并根据设定的阈值排除掉不是相同形状物体的干扰。
void ShapeTemplateMatch(Mat image, vector imgTemplatecontours, double thresh)
{
//灰度化
Mat gray_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
//二值化
Mat thresh_img;
threshold(gray_img, thresh_img, 0, 255, THRESH_OTSU);
//寻找边界
vector> contours_img;
vector hierarchy;
findContours(thresh_img, contours_img, hierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point());
//根据形状模板进行匹配
int min_pos = -1;
for (int i = 0; i < contours_img.size(); i++)
{
//计算轮廓面积,筛选掉一些没必要的小轮廓
// 根据计算面积以及可视化结果确定下面的阈值
//cout << contourArea(contours_img[i]) << endl;
if (contourArea(contours_img[i]) > 120)
{
//进行形状匹配,形状越相似value越小
double value = matchShapes(contours_img[i], imgTemplatecontours, CONTOURS_MATCH_I3, 0.0);
//查看value以及边界图得到thresh阈值
//cout << value << endl;
//将匹配分值与设定阈值比较
if (value < thresh)
{
min_pos = i;
//绘制目标边界
drawContours(image, contours_img, min_pos, Scalar(0, 255,0), 5, 8, hierarchy, 0);
imshow(\"1\", image);
}
}
}
waitKey(0);
}
和上面的函数一样,只是改了一下阈值
int main() {
Mat src = imread(\"D:/9_2.jpg\");
resize(src, src, Size(src.cols / 4, src.rows / 4));
vector tempContours =TemplateContours(src.clone());
//圆垫阈值0.03,瓜子阈值0.2
ShapeTemplateMatch(src.clone(),tempContours, 0.2);
return 0;
}