OpenCV.js 轮廓(Contours)完整指南:查找、绘制、特征提取、属性计算与层级解析
本篇基于 OpenCV 官方 JavaScript 教程(doc/js_tutorials/js_imgproc/js_contours/ 目录下的轮廓专题系列)整理成文,覆盖从"什么是轮廓"到 cv.findContours / cv.drawContours 的基础用法、矩与质心、面积、周长、多边形近似、凸包等特征函数,再到 Solidity、Extent、Mask、平均灰度等派生属性,凸包缺陷、pointPolygonTest、matchShapes 等进阶函数,以及 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.RetrievalModes(RETR_LIST、RETR_EXTERNAL、RETR_CCOMP、RETR_TREE),见本文第 11 节 |
method |
轮廓逼近方法,见 cv.ContourApproximationModes(CHAIN_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_8、cv.LINE_4、cv.LINE_AA,见 cv.LineTypes) |
hierarchy |
可选的层级信息。只有当想借助 maxLevel 只绘制部分轮廓时才需要 |
maxLevel |
绘制轮廓的最大层级。为 0 时只绘制指定轮廓本身;为 1 时绘制该轮廓及其所有嵌套轮廓;为 2 时再包含"嵌套的嵌套",依此类推。仅在提供了 hierarchy 时生效 |
offset |
可选的轮廓平移参数 |
cv.drawContours 不局限于轮廓:只要你拥有一组边界点,就可以用它绘制任意形状。
2.3 轮廓逼近方法(Contour Approximation Method)
method 是 cv.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 节三条约定的落地方式:
cv.cvtColor把 RGBA 转为灰度,cv.threshold(src, src, 120, 200, cv.THRESH_BINARY)生成二值图,使目标为白色、背景为黑色;cv.findContours使用cv.RETR_CCOMP+cv.CHAIN_APPROX_SIMPLE,得到cv.MatVector轮廓集合与cv.Mat层级数组;- 循环中对每个轮廓用随机颜色调用
cv.drawContours,并传入hierarchy与maxLevel = 100,保证嵌套层级都能被绘制出来; - OpenCV.js 中所有 Mat 类型对象都是手工管理内存的,用完必须调用
delete()(这里删除了src、dst、contours、hierarchy),避免浏览器端内存累积——这是 JS 绑定相对桌面端 API 最容易踩的坑。
从源码结构看,cv 命名空间下的这些轮廓函数由 modules/js 模块把 C++ 接口绑定到 WASM 运行时生成(绑定字典见 gen_dict.json,其中登记了 findContours、contourArea、convexHull、matchShapes、pointPolygonTest、convexityDefects 等函数),其底层实现位于 modules/imgproc/src/ 下的 contours_truco.cpp、contours_new.cpp 与 contours_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),公式为:
对应代码:
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.html 与 js_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 |
角度的足够精度;reps 与 aeps 取 0.01 是常用默认值 |
cv.line 的 pt1/pt2 是线段两端点,thickness 为线宽,shift 是点坐标中的小数位数。示例页面:js_contour_features_fitLine.html。
9. 轮廓派生属性:Solidity、Extent、等效直径、朝向、Mask 与平均灰度
以下属性是形状描述中最常用的一组"比值指标",全部可由前面第 4~8 节的 API 组合得到。
9.1 长宽比(Aspect Ratio)
物体包围矩形的宽与高之比:
let rect = cv.boundingRect(cnt);
let aspectRatio = rect.width / rect.height;
9.2 充实度(Extent)
轮廓面积与包围矩形面积之比:
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)
轮廓面积与其凸包面积之比:
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)
面积与轮廓面积相同的圆的直径:
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 / mean 的 mask 参数使用。
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_LIST 或 cv.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 层级信息的实际用途
层级数组并非摆设,它在两处被直接消费:
cv.drawContours的maxLevel参数(见 2.2 节):只有传入hierarchy时才能按层级选择性绘制——例如只想画外部轮廓而不画孔洞边界时,可用RETR_EXTERNAL简化,或借助层级控制maxLevel;- 算法选择:只关心目标外轮廓(如计数、检测)时用
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.markdown、js_contour_features.markdown、js_contour_properties.markdown、js_contours_more_functions.markdown、js_contours_hierarchy.markdown;代码片段与交互示例均取自 doc/js_tutorials/js_assets/ 下的真实 HTML 示例页,底层 C++ 实现可进一步参阅 modules/imgproc/src/ 中的轮廓源文件与 test_contours_new.cpp 等测试用例。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
