OpenCV.js 实战教程:用 JavaScript 提取轮廓的 8 大几何属性(长宽比、填充度、等效直径等)
【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv
本文基于 OpenCV 官方 JavaScript 教程 Contour Properties 展开,讲解如何基于 OpenCV.js(OpenCV 的 WebAssembly 绑定)提取轮廓对象的常用几何属性:长宽比(Aspect Ratio)、填充度(Extent)、实心度(Solidity)、等效直径(Equivalent Diameter)、方向角(Orientation),以及基于 Mask 的极值点定位与平均颜色/灰度计算。读完后,你将掌握一套完整的“从检测到度量”的 Web 端轮廓分析流程,并能对照仓库中的绑定源码理解每个 JS API 背后的真实实现。
1. 前置知识:OpenCV.js 与轮廓系列教程的位置
OpenCV 官方提供基于 WebAssembly 的 JavaScript 绑定模块(位于 modules/js),加载后即可在浏览器中使用全局cv命名空间,调用与 C++ 版 OpenCV 高度同名的 API(如cv.boundingRect、cv.contourArea、cv.convexHull、cv.fitEllipse等)。
本篇是 JS 版 imgproc 教程中“轮廓(Contours)”系列的中间环节:
- 前置环节:使用
cv.findContours找到轮廓、使用cv.contourArea/cv.boundingRect/cv.arcLength等基础轮廓特征函数(对应教程@prev_tutorial{tutorial_js_contour_features}); - 本篇主题:在“轮廓”这一抽象之上,进一步派生出面向物体度量的 8 类常用属性;
- 后续环节:更多轮廓函数,如凸性缺陷(
@next_tutorial{tutorial_js_contours_more_functions})。
核心思想是:大多数高级形状描述符并不是新的底层算法,而是由“轮廓 + 基础几何 API”组合而成的派生量。理解了这一点,就能在 Web 场景中自行扩展出任意需要的形状指标。
2. 属性一:长宽比(Aspect Ratio)
长宽比定义为物体外接矩形(bounding rectangle)宽度与高度之比:
[Aspect ; Ratio = \frac{Width}{Height}]
JS 实现只需两步——先用cv.boundingRect(cnt)取得轮廓的最小外接轴对齐矩形,再取width / height:
let rect = cv.boundingRect(cnt); let aspectRatio = rect.width / rect.height;取值直觉:正方形 ≈ 1.0;横向长条物 > 1.0;纵向细长物 < 1.0。它是成本最低的粗筛指标,例如在工业视觉中先用长宽比区分“片状物/条状物”,再对可疑目标做精细测量。
3. 属性二:填充度(Extent)
填充度是轮廓面积与其外接矩形面积之比,刻画“物体填满了外接矩形多少”:
[Extent = \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;参考值:实心矩形 ≈ 1.0,实心圆 ≈ π/4 ≈ 0.785,三角形 ≈ 0.5。实现要点是cv.contourArea(cnt, false)的第二个参数oriented:传false时按多边形顶点顺序计算(有向面积取绝对值),这与教程给出的签名一致。注意原文示例中cv.boundingRect(cnt));多写了一个右括号,正确写法如上。
4. 属性三:实心度(Solidity)
实心度是轮廓面积与其凸包(convex hull)面积之比,用于衡量物体“凹凸程度”:
[Solidity = \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 ∈ (0, 1],凸多边形为 1.0,缺口越多值越小;典型应用是用“低 Solidity”识别被咬合、粘连或带凹槽的目标;
cv.convexHull(cnt, hull, false, true)的第三个参数clockwise控制凸包环绕方向,第四个参数returnPoints为true时hull输出凸包顶点集合,可直接再喂给cv.contourArea;为false时输出顶点的索引。
5. 属性四:等效直径(Equivalent Diameter)
等效直径定义为“与轮廓面积相同的圆”的直径:
[Equivalent ; Diameter = \sqrt{\frac{4 \times Contour ; Area}{\pi}}]
let area = cv.contourArea(cnt, false); let equiDiameter = Math.sqrt(4 * area / Math.PI);它是把任意形状“归一化”为单一尺度指标的经典手段,在颗粒分析、缺陷面积量化中常用来与固定阈值直接比较,避免逐形状处理尺寸差异。
6. 属性五:方向角与主/次轴(Orientation)
方向角表示物体的朝向。教程使用cv.fitEllipse(最小二乘拟合椭圆)来获取,同时可得到主轴(Major Axis)与次轴(Minor Axis)长度:
let rotatedRect = cv.fitEllipse(cnt); let angle = rotatedRect.angle;fitEllipse返回的RotatedRect包含center、size(对应主轴/次轴长度)与angle。若只需要轴对齐旋转矩形,也可以改用cv.minAreaRect(cnt);两者在轮廓点较少(少于 5 点)时行为不同,可按需选择。方向角常用于判断物体旋转姿态、统计缺陷裂纹走向等。
7. 属性六:Mask 与像素点集(cv.transpose 演示)
在很多后续统计(极值、均值)中,我们需要把物体内部的像素单独“圈”出来作为掩码(Mask)子集。教程 JS 版用cv.transpose(src, dst)演示了“整幅矩阵按行列转置”这类像素级重排操作:
src:输入数组;dst:与src同类型的输出数组。
教程配套了一个可交互的 HTML 演示页 js_contour_properties_transpose.html,内置完整示例代码:
let src = cv.imread('canvasInput'); let dst = new cv.Mat(); cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0); cv.threshold(src, src, 120, 200, cv.THRESH_BINARY); cv.transpose(src, dst); cv.imshow('canvasOutput', dst); src.delete(); dst.delete();这段代码的典型用法链条是:cvtColor灰度化 →threshold二值化得到前景掩码 → 对矩阵做变换处理 →imshow渲染到<canvas>。演示页通过utils.executeCode在浏览器中直接执行修改后的代码(见该 HTML 内的Try it按钮逻辑),是理解 OpenCV.js 内存管理(src.delete()/dst.delete()手动释放 Wasm 堆内存)的现成样板。
8. 属性七:极值及其位置(cv.minMaxLoc)
在得到掩码后,常用cv.minMaxLoc求出(子区域)内的最大/最小值及其坐标:
let result = cv.minMaxLoc(src, mask); let minVal = result.minVal; let maxVal = result.maxVal; let minLoc = result.minLoc; let maxLoc = result.maxLoc;参数说明:src为单通道输入数组;mask为可选掩码,用于只统计子区域。
源码佐证:在 OpenCV.js 的 C++ 绑定层 modules/js/src/core_bindings.cpp 中可以看到该函数有两个重载——带掩码的minMaxLoc(src, mask)与不带掩码的minMaxLoc(src),内部都调用cv::minMaxLoc填充MinMaxLoc结构体(minVal/maxVal/minLoc/maxLoc四个字段),并在 绑定注册处 通过function("minMaxLoc", select_overload<...>)以select_overload区分两个重载导出给 JS 侧。这解释了为何教程示例的返回值是一个含四个字段的对象。
9. 属性八:平均颜色 / 平均灰度(cv.mean)
最后,用同一个掩码计算物体的平均颜色(彩色图)或平均灰度(灰度图):
let average = cv.mean(src, mask);参数说明:src的通道数为 1~4,这样结果才能存入Scalar;mask为可选运算掩码。对 RGBA/RGB 输入,返回的Scalar逐通道给出均值,可用于颜色聚类、光照一致性校验等场景。
10. 组合实战:一次性计算全部形状指标
把上述 API 串起来,即得到一段可直接在浏览器控制台运行的度量代码(假设cnt已由cv.findContours产生,hull为new cv.MatIntVector()):
// 1) 基础量 let area = cv.contourArea(cnt, false); let rect = cv.boundingRect(cnt); let hull = new cv.MatIntVector(); cv.convexHull(cnt, hull, false, true); let hullArea = cv.contourArea(hull, false); let ellipse = cv.fitEllipse(cnt); // 2) 派生属性 let aspectRatio = rect.width / rect.height; let extent = area / (rect.width * rect.height); let solidity = area / hullArea; let equiDiameter = Math.sqrt(4 * area / Math.PI); let orientation = ellipse.angle; // 3) 基于 Mask 的统计(src 为原始灰度图,mask 为二值化前景) let mm = cv.minMaxLoc(src, mask); let mean = cv.mean(src, mask); console.log({ aspectRatio, extent, solidity, equiDiameter, orientation, minVal: mm.minVal, maxLoc: mm.maxLoc, mean });工程上的三条实用建议:
- 先粗筛后精测:
extent、aspectRatio计算代价最低,先用它们过滤,再对少量候选目标计算solidity(需凸包)与fitEllipse; - 掩码复用:
mask一次生成后,minMaxLoc、mean可多次复用,避免重复二值化; - Wasm 内存纪律:所有
new cv.Mat()/new cv.MatIntVector()创建的对象用完后必须delete()(见第 7 节示例的写法),否则会累积 WebAssembly 堆内存,这与 js_assets 下各示例 的统一风格一致。
11. 小结与延伸阅读
本文完整覆盖了教程中的 8 个轮廓属性:
| 属性 | 公式核心 | 关键 API |
|---|---|---|
| 长宽比 | Width / Height | cv.boundingRect |
| 填充度 | 轮廓面积 / 外接矩形面积 | cv.contourArea+cv.boundingRect |
| 实心度 | 轮廓面积 / 凸包面积 | cv.convexHull+cv.contourArea |
| 等效直径 | √(4·Area/π) | cv.contourArea |
| 方向角 | 拟合椭圆角度 | cv.fitEllipse |
| Mask/像素点 | 掩码区域重排演示 | cv.transpose |
| 极值与位置 | 区域 min/max 及坐标 | cv.minMaxLoc |
| 平均颜色/灰度 | 区域均值 | cv.mean |
所有度量都构建在findContours输出之上,属于“派生指标”而非独立算法。想继续深入,可以从以下仓库位置入手:
- 教程原文:本节全部内容的官方表述;
- 交互演示页:可在浏览器中直接改代码运行的
cv.transpose示例; - modules/js/src/core_bindings.cpp:OpenCV.js 手写绑定层,
minMaxLoc、mean等函数的 Wasm 导出实现; - modules/js/generator/embindgen.py:JS 绑定的自动生成脚本,说明大部分
cv.*API 由 C++ 头文件扫描后批量生成。
【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考