OpenCV matchShapes对高度相似轮廓返回244大差异值的原因排查
问题:OpenCV matchShapes返回异常大的匹配值
我有两张高度相似的图片,肉眼看完全匹配。提取出的轮廓视觉上也高度相似,但用cv::matchShapes计算的匹配结果却高达244,数值异常巨大,请问这是代码问题还是其他原因?
我的C++代码
vector<vector<cv::Point>> LoadImage1(cv::Mat m) { cv::Mat g_gray, g_blur, g_binary; cv::cvtColor(m, g_gray, cv::COLOR_BGR2GRAY, 1); cv::blur(g_gray, g_blur, cv::Size2i(3, 3)); cv::threshold(g_blur, g_binary, 80, 255, cv::THRESH_BINARY_INV); vector< vector< cv::Point> > cnt; cv::findContours(g_binary, cnt, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); return cnt; } int main() { cv::Mat m1 = cv::imread("C:\\..Here First image..jpg", cv::IMREAD_COLOR); cv::Mat m2 = cv::imread("C:\\..Here Second image..jpg", cv::IMREAD_COLOR); vector<vector<cv::Point>> cnt1 = LoadImage1(m1); vector<vector<cv::Point>> cnt2 = LoadImage1(m2); cv::drawContours(m1, cnt1, -1, cv::Scalar::all(255)); cv::drawContours(m2, cnt2, -1, cv::Scalar::all(255)); double res = cv::matchShapes(cnt1[0], cnt2[0], cv::CONTOURS_MATCH_I1, 0); printf("res=%f", res); while (true) { if (cv::waitKey(100 /* milliseconds */) == 27) break; } }
问题原因分析
出现这种情况大概率是代码存在以下问题:
- 轮廓顺序不匹配:
cv::findContours返回的轮廓列表顺序由图像中轮廓的面积、位置等因素决定,直接取cnt1[0]和cnt2[0]匹配,很可能这两个轮廓并不是你视觉上认为的相似轮廓(比如一个是图像外边框,一个是目标物体轮廓),形状差异极大导致匹配值异常。 - 缺少轮廓筛选:没有对提取的轮廓做过滤,图像中可能存在噪点生成的小轮廓或背景边框类的大轮廓,直接用第一个轮廓匹配完全是随机行为。
- 轮廓未做归一化处理:虽然
matchShapes对缩放、旋转不敏感,但如果两个轮廓的重心未对齐,或者点集顺序(顺时针/逆时针)不一致,也可能导致计算结果偏离预期。
修复建议
- 筛选目标轮廓
提取轮廓后,通过面积过滤掉干扰轮廓,保留符合预期的目标轮廓:
vector<vector<cv::Point>> filterContours(const vector<vector<cv::Point>>& contours) { vector<vector<cv::Point>> filtered; for (const auto& cnt : contours) { double area = cv::contourArea(cnt); // 根据实际场景调整面积阈值,过滤噪点和边框 if (area > 1000 && area < 100000) { filtered.push_back(cnt); } } return filtered; }
在main函数中替换为:
vector<vector<cv::Point>> cnt1 = filterContours(LoadImage1(m1)); vector<vector<cv::Point>> cnt2 = filterContours(LoadImage1(m2));
验证匹配的轮廓对象
筛选后,先通过绘制轮廓确认cnt1[0]和cnt2[0]是同一类目标,再执行匹配计算。可选:轮廓归一化
如果问题仍存在,可对轮廓做归一化处理,统一位置和尺度:
cv::Point2f getCentroid(const vector<cv::Point>& cnt) { cv::Moments m = cv::moments(cnt); return cv::Point2f(m.m10/m.m00, m.m01/m.m00); } vector<cv::Point> normalizeContour(const vector<cv::Point>& cnt) { cv::Point2f centroid = getCentroid(cnt); vector<cv::Point> normalized; // 平移到原点 for (const auto& p : cnt) { normalized.push_back(cv::Point(p.x - centroid.x, p.y - centroid.y)); } // 缩放统一尺寸 double maxDist = 0; for (const auto& p : normalized) { double dist = cv::norm(cv::Point2f(p)); if (dist > maxDist) maxDist = dist; } if (maxDist > 0) { for (auto& p : normalized) { p.x = static_cast<int>(p.x / maxDist * 100); p.y = static_cast<int>(p.y / maxDist * 100); } } return normalized; }
匹配时使用归一化后的轮廓:
vector<cv::Point> normCnt1 = normalizeContour(cnt1[0]); vector<cv::Point> normCnt2 = normalizeContour(cnt2[0]); double res = cv::matchShapes(normCnt1, normCnt2, cv::CONTOURS_MATCH_I1, 0);
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

