首页
/ OpenCV.js 轮廓(Contours)完整指南:查找、绘制、特征提取、属性计算与层级解析

OpenCV.js 轮廓(Contours)完整指南:查找、绘制、特征提取、属性计算与层级解析

2026-09-04 12:18:19作者:仰钰奇

本篇基于 OpenCV 官方 JavaScript 教程(doc/js_tutorials/js_imgproc/js_contours/ 目录下的轮廓专题系列)整理成文,覆盖从"什么是轮廓"到 cv.findContours / cv.drawContours 的基础用法、矩与质心、面积、周长、多边形近似、凸包等特征函数,再到 Solidity、Extent、Mask、平均灰度等派生属性,凸包缺陷、pointPolygonTestmatchShapes 等进阶函数,以及 Contour Hierarchy(轮廓层级)与四种检索模式(RETR_LIST / RETR_EXTERNAL / RETR_CCOMP / RETR_TREE)的完整原理。读完并动手跑通文中的浏览器端示例后,你将掌握在 OpenCV.js 中完成一套完整轮廓分析流水线(阈值二值化 → 查找轮廓 → 绘制 → 特征/属性计算 → 形状匹配 → 层级遍历)的全部能力。

1. 什么是轮廓

轮廓(Contour)可以简单地解释为:将边界上所有连续点(颜色或灰度相同)连接起来的曲线。轮廓是形状分析和目标检测、识别的实用工具。

在浏览器端使用 cv.findContours 前,有三条来自官方教程的关键约定:

  • 为提高精度,请使用二值图像。因此在查找轮廓之前,应先做阈值化(threshold)或 Canny 边缘检测;
  • 从 OpenCV 3.2 起,源图像不会被该函数修改,可以放心复用输入图像;
  • 在 OpenCV 中,查找轮廓类似于"从黑色背景中查找白色物体"。要查找的目标必须是白色(非零),背景必须是黑色(零)

2. cv.findContours 与 cv.drawContours 接口

2.1 cv.findContours 参数

cv.findContours(image, contours, hierarchy, mode, method, offset = new cv.Point(0, 0))
参数 说明
image 源图像,8-bit 单通道图像。非零像素被视为 1,零像素保持 0,即图像按二值图处理
contours 检测到的轮廓,以 cv.MatVector 形式输出,每个元素是一个 cv.Mat(点集)
hierarchy 包含图像拓扑(父子层级)信息的数组,元素个数等于轮廓个数
mode 轮廓检索模式,见 cv.RetrievalModesRETR_LISTRETR_EXTERNALRETR_CCOMPRETR_TREE),见本文第 11 节
method 轮廓逼近方法,见 cv.ContourApproximationModesCHAIN_APPROX_NONE / CHAIN_APPROX_SIMPLE),见 2.3 节
offset 可选,所有轮廓点的统一偏移量。当轮廓是从图像 ROI 中提取、但需要在全图坐标系中分析时非常有用

2.2 cv.drawContours 参数

cv.drawContours(image, contours, contourIdx, color, thickness = 1, lineType = cv.LINE_8, hierarchy = new cv.Mat(), maxLevel = INT_MAX, offset = new cv.Point(0, 0))
参数 说明
image 目标图像(绘制上去的图像)
contours 全部输入轮廓(cv.MatVector
contourIdx 指定要绘制哪一个轮廓;为负数时绘制全部轮廓
color 轮廓颜色(cv.Scalar
thickness 绘制线宽;为负值时填充轮廓内部
lineType 线连接方式(cv.LINE_8cv.LINE_4cv.LINE_AA,见 cv.LineTypes
hierarchy 可选的层级信息。只有当想借助 maxLevel 只绘制部分轮廓时才需要
maxLevel 绘制轮廓的最大层级。为 0 时只绘制指定轮廓本身;为 1 时绘制该轮廓及其所有嵌套轮廓;为 2 时再包含"嵌套的嵌套",依此类推。仅在提供了 hierarchy 时生效
offset 可选的轮廓平移参数

cv.drawContours 不局限于轮廓:只要你拥有一组边界点,就可以用它绘制任意形状。

2.3 轮廓逼近方法(Contour Approximation Method)

methodcv.findContours 的第五个参数,决定轮廓点的存储粒度。轮廓保存的是形状边界的 (x, y) 坐标,但是否保存全部坐标点由该参数指定:

  • 传入 cv.CHAIN_APPROX_NONE:保存边界上的所有点;
  • 传入 cv.CHAIN_APPROX_SIMPLE移除所有冗余点并压缩轮廓,从而节省内存

教程给出的例子很直观:如果找到的是一条直线的轮廓,表示这条线并不需要线上所有点,只需要两个端点即可。CHAIN_APPROX_SIMPLE 正是做了这种压缩。

3. 浏览器端完整示例:查找并绘制轮廓

教程仓库附带了可直接运行的交互页面 js_contours_begin_contours.html(页面中内置了 canvasInput / canvasOutput 两个画布、Try it 按钮和可编辑代码区,默认载入 lena.jpg,也可通过文件选择框换图)。其核心代码片段如下:

let src = cv.imread('canvasInput');
let dst = cv.Mat.zeros(src.rows, src.cols, cv.CV_8UC3);
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
cv.threshold(src, src, 120, 200, cv.THRESH_BINARY);

let contours = new cv.MatVector();
let hierarchy = new cv.Mat();
// You can try more different parameters
cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

// draw contours with random Scalar
for (let i = 0; i < contours.size(); ++i) {
    let color = new cv.Scalar(Math.round(Math.random() * 255),
                              Math.round(Math.random() * 255),
                              Math.round(Math.random() * 255));
    cv.drawContours(dst, contours, i, color, 1, cv.LINE_8, hierarchy, 100);
}
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); contours.delete(); hierarchy.delete();

这段代码完整体现了第 1 节三条约定的落地方式:

  1. cv.cvtColor 把 RGBA 转为灰度,cv.threshold(src, src, 120, 200, cv.THRESH_BINARY) 生成二值图,使目标为白色、背景为黑色;
  2. cv.findContours 使用 cv.RETR_CCOMP + cv.CHAIN_APPROX_SIMPLE,得到 cv.MatVector 轮廓集合与 cv.Mat 层级数组;
  3. 循环中对每个轮廓用随机颜色调用 cv.drawContours,并传入 hierarchymaxLevel = 100,保证嵌套层级都能被绘制出来;
  4. OpenCV.js 中所有 Mat 类型对象都是手工管理内存的,用完必须调用 delete()(这里删除了 srcdstcontourshierarchy),避免浏览器端内存累积——这是 JS 绑定相对桌面端 API 最容易踩的坑。

从源码结构看,cv 命名空间下的这些轮廓函数由 modules/js 模块把 C++ 接口绑定到 WASM 运行时生成(绑定字典见 gen_dict.json,其中登记了 findContourscontourAreaconvexHullmatchShapespointPolygonTestconvexityDefects 等函数),其底层实现位于 modules/imgproc/src/ 下的 contours_truco.cppcontours_new.cppcontours_link.cpp。浏览器端教程示例使用的 findContours(Truco 算法)与 C++ 端 cv::findContours 是同一实现,行为与本文第 1 节所述约定一致。

4. 矩(Moments)与质心

图像矩用于计算目标的质量中心(质心)、面积等特征。对应的 JS API:

cv.moments(array, binaryImage = false)
参数 说明
array 灰度图像(单通道、8-bit 或浮点二维数组),或 1×N / N×1 的二维点集
binaryImage true 时,所有非零像素都按 1 处理;该参数仅对图像输入有效

从矩中可以提取质心(centroid),公式为:

Cx=M10M00,Cy=M01M00C_x = \frac{M_{10}}{M_{00}}, \quad C_y = \frac{M_{01}}{M_{00}}

对应代码:

let cx = M.m10 / M.m00;
let cy = M.m01 / M.m00;

可运行示例见 js_contour_features_moments.html

5. 轮廓面积与周长

5.1 面积:cv.contourArea

轮廓面积可由 cv.contourArea() 直接计算,也可以从矩中取 M['m00']

cv.contourArea(contour, oriented = false)
参数 说明
contour 输入二维点向量(轮廓顶点)
oriented 有向面积标志。为 true 时按轮廓方向(顺时针/逆时针)返回带符号的面积,可用符号判断轮廓方向;默认 false 返回绝对值

示例页面 js_contour_features_area.html 的实际用法(对 contours.get(20) 取出的某个轮廓求面积):

let area = cv.contourArea(cnt, false);

5.2 周长:cv.arcLength

周长(也叫弧长,arc length)由 cv.arcLength() 求得:

cv.arcLength(curve, closed)
参数 说明
curve 输入二维点向量
closed 曲线是否闭合的标志位

示例页面:js_contour_features_perimeter.html

6. 多边形近似:cv.approxPolyDP

cv.approxPolyDP 按指定的精度把轮廓逼近为顶点更少的另一个形状,其算法是 Douglas-Peucker(Ramer-Douglas-Peucker)算法的实现。

cv.approxPolyDP(curve, approxCurve, epsilon, closed)
参数 说明
curve 输入的二维点集(存于 cv.Mat
approxCurve 近似结果,类型应与输入曲线一致
epsilon 近似精度,即原始曲线与其近似之间的最大距离
closed true 时近似曲线是闭合的(首尾顶点相连)

epsilon 越大,顶点越少、形状越粗糙;它常被用来"数顶点"识别形状(如 3 个顶点近似为三角形、4 个为四边形)。示例页面:js_contour_features_approxPolyDP.html

7. 凸包与凸性判断

7.1 cv.convexHull

凸包(Convex Hull)看起来很像轮廓近似,但两者并不相同(某些情况下可能给出相同结果)。cv.convexHull() 检查曲线的凸性缺陷并加以"纠正"。一般地,凸曲线总是向外凸出(或至少是平的);向内凹进的部分称为凸性缺陷(convexity defects)。官方教程以手背图像示意:红线是手的凸包,双向箭头标记处即凸包与轮廓之间的局部最大偏离,见 凸包与凸性缺陷示意图

手背图像的凸包(红线)与凸性缺陷(双向箭头)示意图

cv.convexHull(points, hull, clockwise = false, returnPoints = true)
参数 说明
points 输入二维点集
hull 输出的凸包
clockwise 方向标志。为 true 时输出凸包按顺时针方向,否则为逆时针。默认坐标系 X 轴向右、Y 轴向上
returnPoints 操作标志。当输出为矩阵时,true 返回凸包上的点;否则返回凸包点对应的索引

示例页面:js_contour_features_convexHull.html

注意:后续第 9.1 节求凸包缺陷时,必须传 returnPoints = false(返回索引),这是官方教程明确强调的。

7.2 cv.isContourConvex

判断一条曲线是否为凸,只需:

cv.isContourConvex(cnt);

返回布尔值,表示该轮廓是否凸。

8. 包围矩形、最小外接圆、椭圆与直线拟合

8.1 两种包围矩形

a. 直包围矩形 cv.boundingRect(points):不考虑目标旋转的轴对齐矩形,因此其面积不一定是包含目标的最小面积。

b. 旋转包围矩形 cv.minAreaRect(points):以最小面积为准则绘制包围矩形,因此会考虑目标旋转。

两者输入均为二维点集(points)。示例页面分别为 js_contour_features_boundingRect.htmljs_contour_features_minAreaRect.html

8.2 最小外接圆:cv.minEnclosingCircle

cv.minEnclosingCircle(points) 求能完全覆盖物体且面积最小的圆。画出该圆配合 cv.circle

cv.minEnclosingCircle(points)
cv.circle(img, center, radius, color, thickness = 1, lineType = cv.LINE_8, shift = 0)
参数(cv.circle) 说明
img 绘制目标图像
center 圆心
radius 半径
color 圆的颜色
thickness 圆轮廓线宽;负值表示画实心圆
lineType 圆边界类型
shift 圆心与半径坐标中的小数位数

示例页面:js_contour_features_minEnclosingCircle.html

8.3 椭圆拟合:cv.fitEllipse

把椭圆拟合到物体上,返回其中内切椭圆所在的旋转矩形:

cv.fitEllipse(points)
cv.ellipse(img, box, color, thickness = 1, lineType = cv.LINE_8)

box 是旋转矩形(cv.RotatedRect)形式的椭圆表示,即绘制内切于该旋转矩形的椭圆;thickness 为正值画椭圆弧线轮廓,否则画填充的椭圆扇区。示例页面:js_contour_features_fitEllipse.html

8.4 直线拟合:cv.fitLine

同样可以对一组点拟合直线:

cv.fitLine(points, line, distType, param, reps, aeps)
cv.line(img, pt1, pt2, color, thickness = 1, lineType = cv.LINE_8, shift = 0)
参数(cv.fitLine) 说明
points 输入二维点集
line 输出的直线参数,应为含 4 个元素 [vx, vy, x0, y0] 的 Mat:[vx, vy] 是与直线共线的归一化向量,[x0, y0] 是直线上一点
distType M-估计器使用的距离类型(见 cv.DistanceTypes
param 某些距离类型的数值参数 C;为 0 时选择最优值
reps 半径(坐标原点到直线的距离)的足够精度
aeps 角度的足够精度;repsaeps0.01 是常用默认值

cv.linept1/pt2 是线段两端点,thickness 为线宽,shift 是点坐标中的小数位数。示例页面:js_contour_features_fitLine.html

9. 轮廓派生属性:Solidity、Extent、等效直径、朝向、Mask 与平均灰度

以下属性是形状描述中最常用的一组"比值指标",全部可由前面第 4~8 节的 API 组合得到。

9.1 长宽比(Aspect Ratio)

物体包围矩形的宽与高之比:

Aspect  Ratio=WidthHeightAspect \; Ratio = \frac{Width}{Height}

let rect = cv.boundingRect(cnt);
let aspectRatio = rect.width / rect.height;

9.2 充实度(Extent)

轮廓面积与包围矩形面积之比:

Extent=Object  AreaBounding  Rectangle  AreaExtent = \frac{Object \; Area}{Bounding \; Rectangle \; Area}

let area = cv.contourArea(cnt, false);
let rect = cv.boundingRect(cnt);
let rectArea = rect.width * rect.height;
let extent = area / rectArea;

Extent 越接近 1,说明物体填充其包围盒越充分(圆形、方形接近 1,细长形接近 0)。

9.3 实体度(Solidity)

轮廓面积与其凸包面积之比:

Solidity=Contour  AreaConvex  Hull  AreaSolidity = \frac{Contour \; Area}{Convex \; Hull \; Area}

let area = cv.contourArea(cnt, false);
cv.convexHull(cnt, hull, false, true);
let hullArea = cv.contourArea(hull, false);
let solidity = area / hullArea;

Solidity 永远不大于 1;缺口、凹陷越多的形状(如字母 C、星形)Solidity 越小。

9.4 等效直径(Equivalent Diameter)

面积与轮廓面积相同的圆的直径:

Equivalent  Diameter=4×Contour  AreaπEquivalent \; Diameter = \sqrt{\frac{4 \times Contour \; Area}{\pi}}

let area = cv.contourArea(cnt, false);
let equiDiameter = Math.sqrt(4 * area / Math.PI);

9.5 朝向(Orientation)

物体指向的角度;该方法同时给出主轴与次轴长度:

let rotatedRect = cv.fitEllipse(cnt);
let angle = rotatedRect.angle;

9.6 Mask 与像素点

某些场合需要拿到组成目标的所有像素点。官方教程在此展示了 cv.transpose(src, dst)src 为输入数组,dst 为与 src 同类型的输出数组),配套交互示例见 js_contour_properties_transpose.html。从该示例页面的定位看,其意图是配合轮廓生成"目标像素掩码",供后面 minMaxLoc / meanmask 参数使用。

9.7 最大/最小值及其位置:cv.minMaxLoc

cv.minMaxLoc(src, mask)
参数 说明
src 输入单通道数组
mask 可选掩码,用于选取子数组(即只在目标区域内统计)
let result = cv.minMaxLoc(src, mask);
let minVal = result.minVal;
let maxVal = result.maxVal;
let minLoc = result.minLoc;
let maxLoc = result.maxLoc;

9.8 平均颜色/平均灰度:cv.mean

求物体的平均颜色(彩色图)或灰度平均强度(灰度图),同样借助 mask:

cv.mean(src, mask)
参数 说明
src 输入数组,应为 1~4 通道,以便结果能存入 Scalar
mask 可选操作掩码
let average = cv.mean(src, mask);

10. 进阶函数:凸包缺陷、点-多边形测试、形状匹配

10.1 凸包缺陷(Convexity Defects)

对象相对于凸包的任何偏离都可以看作凸包缺陷。可视化方式是:连接缺陷的起点与终点画一条线,再在最远点处画圆。

注意:求凸包缺陷时,求凸包必须传 returnPoints = false(返回索引)

cv.convexityDefects(contour, convexhull, convexityDefect)
参数 说明
contour 输入轮廓
convexhull convexHull 求得的凸包,必须包含构成凸包的轮廓点索引
convexityDefect 输出的凸包缺陷向量。每个缺陷表示为一个 4 元组 (start_index, end_index, farthest_pt_index, fixpt_depth):三个索引都是原始轮廓中缺陷起点、终点、最远点的 0 基索引;fixpt_depth 是最远轮廓点到凸包距离的定点数近似(8 位小数),要得到浮点深度需计算 fixpt_depth / 256.0

交互示例:js_contours_more_functions_convexityDefects.html

10.2 点-多边形测试(Point Polygon Test)

求图像中一点到轮廓的最短距离。返回的带符号距离为:点在轮廓外为负,在轮廓内为正,恰在轮廓上为零

cv.pointPolygonTest(contour, pt, measureDist)
参数 说明
contour 输入轮廓
pt 被测试的点
measureDist true 时估计点到最近轮廓边的带符号距离;否则只判断点在轮廓内/外
let dist = cv.pointPolygonTest(cnt, new cv.Point(50, 50), true);

10.3 形状匹配(Match Shapes)

cv.matchShapes() 可以比较两个形状(或两个轮廓),返回一个相似度度量:结果越小,匹配越好。该度量基于 Hu 矩(Hu-moment) 计算。

cv.matchShapes(contour1, contour2, method, parameter)
参数 说明
contour1 第一个轮廓或灰度图像
contour2 第二个轮廓或灰度图像
method 比较方法,见 cv::ShapeMatchModes
parameter 方法相关参数(当前不支持)

交互示例:js_contours_more_functions_shape.html

11. 轮廓层级(Contour Hierarchy)

11.1 什么是层级

cv.findContours() 查找轮廓时有一个参数"轮廓检索模式"(Contour Retrieval Mode),大家通常随手传 cv.RETR_LISTcv.RETR_TREE 就能正常工作——但它到底意味着什么?另外,输出中有三个数组:图像、轮廓,还有一个命名为 hierarchy 的输出,却从没有被用到过。这一节解释它是什么、为什么存在,以及它与检索模式参数的关系。

轮廓之间存在位置关系:有些形状位于其他形状内部,就像嵌套图形。此时称外层的为父(parent),内层的为子(child)。轮廓之间由此形成父子关系,这种关系用**层级(Hierarchy)**来表示。

官方教程的示例图(images/hierarchy.png)中有编号 0~5 的若干形状,其中 2 和 2a 分别表示最外层方框的外轮廓与内轮廓:

  • 轮廓 0、1、2 是外部(最外层)轮廓,它们处于同一层级(hierarchy-0)
  • 轮廓 2a 可视为轮廓 2 的(反过来说 2 是 2a 的父),处于 hierarchy-1;
  • 轮廓 3 是 2a 的子,处于下一层级;
  • 轮廓 4、5 是 3a 的子,处于最末层级(按编号顺序,轮廓 4 可视为 3a 的第一个子,也可能是轮廓 5)。

由此引出一组术语:同一层级、外部轮廓、子轮廓、父轮廓、第一个子等。

11.2 OpenCV 中的层级表示:[Next, Previous, First_Child, Parent]

每条轮廓都携带"它在哪一层、它的子是谁、它的父是谁"等信息。OpenCV 将其表示为四值数组 [Next, Previous, First_Child, Parent]

  • Next:同一层级中的下一条轮廓。例如轮廓 0 的同层下一条是轮廓 1,故 Next = 1;轮廓 1 的 Next = 2;轮廓 2 没有同层下一条,Next = -1;轮廓 4 与轮廓 5 同层,Next = 5
  • Previous:同一层级中的上一条轮廓。轮廓 1 的上一条是轮廓 0;轮廓 2 的上一条是轮廓 1;轮廓 0 没有上一条,为 -1
  • First_Child:它的第一个子轮廓。轮廓 2 的子是 2a,取 2a 的索引;轮廓 3a 有两个子,只取第一个,即 First_Child = 4
  • Parent:其父轮廓的索引,与 First_Child 相反。轮廓 4 和 5 的父都是 3a;3a 的父是 3,依此类推。

若没有子或父,对应字段取 -1

11.3 四种检索模式逐一解读

1. RETR_LIST —— 四个标志中最简单的一个:检索所有轮廓,但不建立任何父子关系。"父子和孩子在此规则下平等,都只是轮廓",即全部属于同一层级。因此层级数组的第 3、4 项(First_Child、Parent)恒为 -1,而 Next/Previous 有对应值。

2. RETR_EXTERNAL —— 只返回最外层轮廓,所有子轮廓都被丢弃。"在这种法则下,每个家庭只有长子被照顾到,其他成员不在考虑之列"。

3. RETR_CCOMP —— 检索所有轮廓并整理成两层层级:物体的外部轮廓(边界)放在 hierarchy-1,物体内部孔洞(如果有)的轮廓放在 hierarchy-2;若物体内部还有物体,其轮廓又放回 hierarchy-1,孔洞再放 hierarchy-2,依此类推。直观例子:黑底上的"大白色零",零的外圈属于第一层级,内圈属于第二层级。

教程给出的 RETR_CCOMP 实例(images/ccomp_hierarchy.png,红色为轮廓编号、绿色为层级)逐条推演层级数组:

  • contour-0(hierarchy-1,有两个孔洞 1、2):同层下一条是 contour-3,无上一条,第一个子是 contour-1,无父。数组为 [3, -1, 1, -1]
  • contour-1(hierarchy-2,在 contour-0 下):同层下一条是 contour-2,无上一条、无子,父是 contour-0。数组为 [2, -1, -1, 0]
  • contour-2(hierarchy-2):在 contour-0 下无同层下一条,上一条是 contour-1,无子,父是 contour-0。数组为 [-1, 1, -1, 0]
  • contour-3:hierarchy-1 中下一条是 contour-5,上一条是 contour-0,子是 contour-4,无父。数组为 [5, 0, 4, -1]
  • contour-4:在 contour-3 下的 hierarchy-2,无同层兄弟。无 next、无 previous、无子,父是 contour-3。数组为 [-1, -1, -1, 3]

4. RETR_TREE —— "完美的先生":检索所有轮廓并构建完整的家族树——"它甚至能告诉你谁是爷爷、父亲、儿子、孙子乃至更远的辈分"。教程把同一张图改用 cv.RETR_TREE 重排轮廓后的逐条推演(images/tree_hierarchy.png):

  • contour-0(hierarchy-0):同层下一条是 contour-7,无上一条,子是 contour-1,无父。数组为 [7, -1, 1, -1]
  • contour-2(hierarchy-1):同层无轮廓,无上一条,子是 contour-2,父是 contour-0。数组为 [-1, -1, 2, 0]

11.4 层级信息的实际用途

层级数组并非摆设,它在两处被直接消费:

  1. cv.drawContoursmaxLevel 参数(见 2.2 节):只有传入 hierarchy 时才能按层级选择性绘制——例如只想画外部轮廓而不画孔洞边界时,可用 RETR_EXTERNAL 简化,或借助层级控制 maxLevel
  2. 算法选择:只关心目标外轮廓(如计数、检测)时用 RETR_EXTERNAL 最少;需要区分"物体"与"孔洞"(如 RETR_CCOMP 的 1/2 两层)时用 RETR_CCOMP;需要完整嵌套关系(如文档布局、嵌套区域分析)时用 RETR_TREE

12. 小结与浏览器端实践要点

把整个轮廓专题串起来,一条典型流水线是:

// 1. 二值化:目标白色、背景黑色
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
cv.threshold(src, src, 120, 200, cv.THRESH_BINARY);

// 2. 查找轮廓(RETR_CCOMP / RETR_TREE 按需选择)
let contours = new cv.MatVector();
let hierarchy = new cv.Mat();
cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

// 3. 逐轮廓计算特征与属性
for (let i = 0; i < contours.size(); ++i) {
    let cnt = contours.get(i);
    let M = cv.moments(cnt, false);                 // 矩 → 质心 (M.m10/M.m00, M.m01/M.m00)
    let area = cv.contourArea(cnt, false);          // 面积
    let peri = cv.arcLength(cnt, true);             // 周长
    let rect = cv.boundingRect(cnt);                // 包围矩形 → Aspect Ratio / Extent
    let hull = cv.convexHull(cnt, null, false, true);
    let solidity = area / cv.contourArea(hull, false);
    // 进一步:cv.approxPolyDP、cv.minAreaRect、cv.minEnclosingCircle、
    //        cv.fitEllipse、cv.fitLine、cv.convexityDefects、
    //        cv.pointPolygonTest、cv.matchShapes ...
    cnt.delete(); hull.delete();
}

// 4. 绘制
cv.drawContours(dst, contours, -1, new cv.Scalar(0, 255, 0), 1);

// 5. 释放全部 Mat
src.delete(); dst.delete(); contours.delete(); hierarchy.delete();

实践要点归纳:

  • 先二值化再找轮廓;OpenCV.js 中 findContours 不修改源图(OpenCV 3.2 起的约定);
  • CHAIN_APPROX_SIMPLE 是默认首选,它用最少冗余点表达轮廓,显著降低内存与后续计算开销;
  • 求凸包缺陷必须 returnPoints = false,且 convexityDefects 返回的 depth 是定点数,需 depth / 256.0 还原;
  • matchShapes 结果越小越相似,其基础是 Hu 矩,因此对平移、旋转、尺度具有一定不变性;
  • 层级数组 [Next, Previous, First_Child, Parent] 中缺失关系一律以 -1 表示,RETR_LIST 的第三、四项恒为 -1;
  • 浏览器端务必对每个 cv.Mat / cv.MatVector 调用 delete(),这是所有 JS 示例页面(如 js_contours_begin_contours.html)在结尾统一执行的原因。

本文全部 API 签名与参数说明继承自官方教程文档 js_table_of_contents_contours.markdown 及其五篇子教程 js_contours_begin.markdownjs_contour_features.markdownjs_contour_properties.markdownjs_contours_more_functions.markdownjs_contours_hierarchy.markdown;代码片段与交互示例均取自 doc/js_tutorials/js_assets/ 下的真实 HTML 示例页,底层 C++ 实现可进一步参阅 modules/imgproc/src/ 中的轮廓源文件与 test_contours_new.cpp 等测试用例。

登录后查看全文
热门项目推荐
相关项目推荐