You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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对缩放、旋转不敏感,但如果两个轮廓的重心未对齐,或者点集顺序(顺时针/逆时针)不一致,也可能导致计算结果偏离预期。

修复建议

  1. 筛选目标轮廓
    提取轮廓后,通过面积过滤掉干扰轮廓,保留符合预期的目标轮廓:
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));
  1. 验证匹配的轮廓对象
    筛选后,先通过绘制轮廓确认cnt1[0]和cnt2[0]是同一类目标,再执行匹配计算。

  2. 可选:轮廓归一化
    如果问题仍存在,可对轮廓做归一化处理,统一位置和尺度:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 14:32:13