算子:
matchShapes( InputArray contour1, InputArray contour2, int method, double parameter )
(1)参数1是待匹配轮廓或者灰度图像
(2)参数2同参数1
(3)比较参数1和2相似度的方法,opencv提供了三种如下:
CV_CONTOURS_MATCH_I1
CV_CONTOURS_MATCH_I2
CV_CONTOURS_MATCH_I3
(4)参数4为目前还不支持,使用时赋个0就ok。
将待识别图像 -> 灰度处理 -> 自动阈值分割
通过轮廓检索函数 cv.findContours 找到待识别图像所有轮廓
模板图像 -> 灰度图像 -> 自动阈值分割
通过轮廓检索函数 cv.findContours 找到模板图像中目标的外轮廓
将第2步得到的轮廓逐一和第4步得到的轮廓 通过 cv.matchShapes 函数进行形状匹配。找到其中最小值,最小值对应的待识别图像中的轮廓即为匹配到的模板图像
标出在待识别图像中找到的模板图像
注意:利用mathshape得到的匹配分值越小,则两个轮廓越相似,越大则越不相似。在匹配过程中会通过匹配分值与设定的分值进行比较,在保证匹配效果完美的情况下,设定的分值越低越好,如果设定的匹配分值略高,可能会导致两个完全不相似的形状匹配成功(亲身实验,分值设高,利用三角形会匹配到圆形)。
另外,matchShapes函数其实比较的是两个轮廓的Hu不变矩。Hu矩特性:具有旋转,缩放和平移不变性。由Hu矩组成的特征量对图片进行识别,优点就是速度很快,缺点是识别率比较低。 因此Hu不变矩一般用来识别图像中大的物体,对于物体的形状描述得比较好,图像的纹理特征不能太复杂。
代码:
(1)创建形状轮廓模板
vector<Point> ImageTemplateContours(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);
//膨胀处理
Mat ellipse = getStructuringElement(MORPH_ELLIPSE, Size(15, 15));
Mat erode_img_template;
//erode(thresh_img_template, erode_img_template, ellipse);
morphologyEx(thresh_img_template, thresh_img_template, MORPH_OPEN, ellipse, Point(-1, -1), 1);
//寻找边界
vector<vector<Point>> contours_template;
vector<Vec4i> hierarchy;
findContours(thresh_img_template, contours_template, hierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point());
//绘制边界
drawContours(img_template, contours_template, 0, Scalar(0, 0, 255), 1, 8, hierarchy);
return contours_template[0];
}
注:之所以返回findContours算子发现的所有轮廓中的第0个,这是因为第0个就是我想要的圆形模板
(2)进行形状模板匹配
vector<Point2d> ShapeTemplateMatch(Mat image, vector<Point> imgTemplatecontours, double minMatchValue)
{
vector<Point2d> image_coordinates;
//灰度化
Mat gray_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
//阈值分割
Mat thresh_img;
threshold(gray_img, thresh_img, 0, 255, THRESH_OTSU);
//寻找边界
vector<vector<Point>> contours_img;
vector<Vec4i> hierarchy;
findContours(thresh_img, contours_img, hierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point());
//根据形状模板进行匹配
int min_pos = -1;
double min_value = minMatchValue;//匹配分值,小于该值则匹配成功
for (int i = 0; i < contours_img.size(); i++)
{
//计算轮廓面积,筛选掉一些没必要的小轮廓
if (contourArea(contours_img[i])>12000)
{
//得到匹配分值
double value = matchShapes(contours_img[i], imgTemplatecontours, CONTOURS_MATCH_I3, 0.0);
//将匹配分值与设定分值进行比较
if (value < min_value)
{
min_pos = i;
//绘制目标边界
drawContours(image, contours_img, min_pos, Scalar(0, 0, 255), 1, 8, hierarchy, 0);
//获取重心点
Moments M;
M = moments(contours_img[min_pos]);
double cX = double(M.m10 / M.m00);
double cY = double(M.m01 / M.m00);
//显示目标中心并提取坐标点
circle(image, Point2d(cX, cY), 1, Scalar(0, 255, 0), 2, 8);
//putText(image, "center", Point2d(cX - 20, cY - 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1, 8);
//将目标的重心坐标都存在数组中
image_coordinates.push_back(Point2d(cX, cY));//向数组中存放点的坐标
}
}
}
return image_coordinates;
}