OpencvSharp 算子学习教案之 - Cv2.MatchShapes 重载1
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.MatchShapes
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:imgproc
- 源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:3491 / 3514
摘要:本页演示MatchShapes(InputArray, InputArray, ShapeMatchModes, double)如何把两个Mat轮廓转成 Hu 矩比较分数,并说明分数越小越相似。
1. 函数名称(带参数签名)
publicstaticdoubleMatchShapes(InputArraycontour1,InputArraycontour2,ShapeMatchModesmethod,doubleparameter=0)2. 函数用途
Cv2.MatchShapes(...)用来比较两个轮廓的形状相似度。
这个重载最适合轮廓已经保存成Mat的场景:
- 输入可以直接来自前面的轮廓矩阵。
- 返回值是一个相似度分数,越小表示越像。
- 可以选择三种方法中的一种来比较。
它常用于:
- 形状检索。
- 目标轮廓筛选。
- 模板匹配的辅助判断。
- 初学者理解 Hu 矩和形状不变性的入门示例。
3. 函数公式
MatchShapes 基于 Hu 矩比较两条轮廓的整体形状。OpenCV 文档把三种方法写成:
m i = sign ( h i ) ⋅ log ( h i ) m_i = \operatorname{sign}(h_i)\cdot \log(h_i)mi=sign(hi)⋅log(hi)
I 1 ( A , B ) = ∑ i = 1 7 ∣ 1 m i A − 1 m i B ∣ I_1(A,B)=\sum_{i=1}^{7}\left|\frac{1}{m_i^A}-\frac{1}{m_i^B}\right|I1(A,B)=i=1∑7miA1−miB1
I 2 ( A , B ) = ∑ i = 1 7 ∣ m i A − m i B ∣ I_2(A,B)=\sum_{i=1}^{7}\left|m_i^A-m_i^B\right|I2(A,B)=i=1∑7miA−miB
I 3 ( A , B ) = max i = 1 7 ∣ m i A − m i B ∣ ∣ m i A ∣ I_3(A,B)=\max_{i=1}^{7}\frac{\left|m_i^A-m_i^B\right|}{\left|m_i^A\right|}I3(A,B)=i=1max7miAmiA−miB
其中A AA和B BB分别表示两个轮廓。
4. 函数原理说明
这个函数的重点,是把轮廓转换成能反映整体形状的 Hu 矩,再根据不同方法算出差异。
对初学者来说,最值得记住的是:
- 它比较的是形状,不是轮廓在图像里的位置。
- 对平移、旋转和缩放通常都比较鲁棒。
- 分数越小,两个轮廓越接近。
parameter目前在 OpenCvSharp 里还没有实际启用,所以教学时可以先把它理解成占位参数。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| contour1 | InputArray | 是 | 第一个轮廓或灰度图像 |
| contour2 | InputArray | 是 | 第二个轮廓或灰度图像 |
| method | ShapeMatchModes | 是 | 形状比较方法 |
| parameter | double | 否 | 方法相关参数,当前未启用 |
补充说明:
- 这里的
InputArray最常见就是Mat形式的轮廓。 - 如果你比较的是轮廓,输入一般用
Point或Point2f点集构造。 ShapeMatchModes有三种常用值:I1、I2和I3。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:轮廓检索 | 找出最像的目标轮廓 | 目标识别 |
| 场景B:形状筛选 | 判断两个对象像不像 | 分类预筛 |
| 场景C:教学对比 | 观察三种方法的分数差异 | OpenCvSharp 入门 |
| 场景D:鲁棒性验证 | 比较变换后的同形轮廓 | 几何分析 |
7. 函数使用示例(与 WPF 场景一一对应)
说明:下面示例对应 WPF 场景 A。它把两个轮廓先包装成
Mat,再分别用三种方法计算相似度。
usingSystem;usingSystem.Globalization;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 参考轮廓直接复用一组较稳定的多边形点,方便读者集中观察“形状比较”这件事。Point2f[]referencePoints={newPoint2f(76.5f,96.0f),newPoint2f(164.0f,58.5f),newPoint2f(262.0f,74.0f),newPoint2f(304.0f,136.0f),newPoint2f(286.0f,214.0f),newPoint2f(196.0f,262.0f),newPoint2f(92.5f,234.0f),newPoint2f(58.0f,150.0f),};// 候选轮廓先做一点缩放和旋转,再轻微拉动一个顶点,故意让它“很像,但不是完全一样”。Point2f[]candidatePoints={newPoint2f(120.0f,82.0f),newPoint2f(188.0f,92.0f),newPoint2f(250.0f,124.0f),newPoint2f(266.0f,194.0f),newPoint2f(212.0f,248.0f),newPoint2f(144.0f,252.0f),newPoint2f(92.0f,198.0f),newPoint2f(88.0f,126.0f),};// MatchShapes 的 InputArray 重载可以直接接收 Mat,所以这里先把两组点包装成矩阵。usingvarreferenceMat=Mat.FromArray(referencePoints);usingvarcandidateMat=Mat.FromArray(candidatePoints);// 三种比较方法都算一遍,初学者就能直观看到每种方法的结果。doublescoreI1=Cv2.MatchShapes(referenceMat,candidateMat,ShapeMatchModes.I1);doublescoreI2=Cv2.MatchShapes(referenceMat,candidateMat,ShapeMatchModes.I2);doublescoreI3=Cv2.MatchShapes(referenceMat,candidateMat,ShapeMatchModes.I3);Console.WriteLine($"ReferenceCount ={referencePoints.Length}");Console.WriteLine($"CandidateCount ={candidatePoints.Length}");Console.WriteLine($"I1 ={scoreI1.ToString("E6",CultureInfo.InvariantCulture)}");Console.WriteLine($"I2 ={scoreI2.ToString("E6",CultureInfo.InvariantCulture)}");Console.WriteLine($"I3 ={scoreI3.ToString("E6",CultureInfo.InvariantCulture)}");Console.WriteLine($"ReferenceArea ={Math.Abs(Cv2.ContourArea(referencePoints)).ToString("F2",CultureInfo.InvariantCulture)}");Console.WriteLine($"CandidateArea ={Math.Abs(Cv2.ContourArea(candidatePoints)).ToString("F2",CultureInfo.InvariantCulture)}");// 这一行是本例的核心:分数越小,两个轮廓越相似。Console.WriteLine("MatchShapes 的结果越接近 0,说明两个形状越像。\n");}}8. 常见错误与避坑
- 把 MatchShapes 误认为位置比较,它实际比较的是形状本身。
- 看到分数不是 0 就以为不对,实际上轻微变形后分数本来就会变化。
- 忽略
method参数,导致无法理解三种评分方式的差异。 - 把
parameter当成必须填写的关键参数,实际上当前版本还没启用。
9. 进阶扩展
- 可以同时比较多组轮廓,再按分数从小到大排序。
- 可以把 MatchShapes 和
FindContours结合,做简单形状检索。 - 可以把三种方法的结果放在同一张表里对比。
- 可以先做轮廓平滑,再比较形状分数,观察结果是否更稳定。
10. 小结
Cv2.MatchShapes(...)的 InputArray 重载适合把已经整理好的轮廓直接拿来做形状比较。
如果你关心“长得像不像”,它通常比简单的面积、周长比较更有意义。
11. 相关链接
- WPF 教学控件:Cv2MatchShapesControl.xaml.cs
- 样例实现:MatchShapesInputArraySample.cs
- 官方文档源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs
- 另一个重载源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs