首页
/ OpenCV.js 完全指南:在 opencv 仓库中构建 WebAssembly 版 OpenCV 并在浏览器中做计算机视觉

OpenCV.js 完全指南:在 opencv 仓库中构建 WebAssembly 版 OpenCV 并在浏览器中做计算机视觉

2026-09-06 11:46:24作者:冯爽妲Honey

本文以 OpenCV 仓库的 OpenCV.js 官方教程体系(doc/js_tutorials)为主线,完整覆盖从 Emscripten 环境搭建、build_js.py 源码构建、各构建开关(threads/simd/WebNN/测试/文档)到浏览器端 API 使用的全部流程,并结合仓库中的构建脚本与交互示例页面讲解 GUI、Core、ImgProc、Video、DNN 五大教程模块。读完之后,你可以独立完成 opencv.js 的预构建使用与自定义构建,并知道如何用 cv 对象在网页中完成图像读写、显示、滤波、边缘检测、目标跟踪与深度学习推理。

OpenCV.js GUI 教程:在网页中加载图片并显示到 canvas 的运行结果

OpenCV.js Trackbar 教程:用滑块实时调节参数的运行结果

OpenCV.js 是什么:OpenCV 的 JavaScript 绑定

OpenCV.js 教程入口页 js_tutorials.markdown 定义了整个教程集的目标:"Learn how to use OpenCV.js inside your web pages!"(学会在网页中使用 OpenCV.js)。要理解它,先理解它背后的编译链路,这部分内容来自 OpenCV.js 介绍

  • Emscripten 是 LLVM-to-JavaScript 编译器。它接收 clang 生成的 LLVM bitcode,编译成可直接在浏览器中执行的 asm.js 或 WebAssembly。WebAssembly 是 W3C 开放标准定义的便携、尺寸与加载效率高的二进制格式,目标是接近原生的执行速度。
  • OpenCV.js 是 OpenCV 函数子集的 JavaScript 绑定,通过 Emscripten 把 OpenCV 的 C++ 函数编译为 WebAssembly 目标,并向 Web 应用暴露 JavaScript API。它面向的场景是:HTML5 video 标签在线播放视频、WebRTC 采集摄像头、canvas API 逐像素访问视频帧——这些应用都需要高效的视觉内核。
  • 项目历史:OpenCV 于 1999 年由 Gary Bradski 在 Intel 创建,2000 年发布首个版本。OpenCV.js 最初诞生于加州大学尔湾分校(UCI)Parallel Architectures and Systems Group 的 Intel 资助研究项目,后在 Google Summer of Code 2017 项目中被改进并正式并入 OpenCV 项目。

从教程介绍页可以看到,OpenCV.js 教程体系的目标是帮助 Web 开发者交互式地理解各类视觉算法:因为 opencv.js 能直接在浏览器中运行,教程页面本身就是可交互的——用 WebRTC API 加 JavaScript 实时改参、实时看到结果。介绍页也注明了适用前提:这套教程以 OpenCV 3.x 版本为主要讲解对象,具备 JavaScript 与 Web 开发基础知识更佳。

教程体系总览:六大模块结构

入口文档 js_tutorials.markdown@subpage 指令挂载了六个子目录,每个子目录都有一个 js_table_of_contents_*.markdown 汇总页。理解这个结构,就掌握了整本教程的骨架:

模块 汇总页(仓库相对路径) 内容定位
Setup js_table_of_contents_setup.markdown 介绍 OpenCV.js、快速上手(Get started)、从源码构建、Node.js 中使用
GUI js_table_of_contents_gui.markdown 加载并显示图像、从摄像头采集并播放视频、创建 trackbar 控制参数
Core js_table_of_contents_core.markdown 像素读写与 ROI 等基本操作、图像算术运算、常用数据结构
ImgProc js_table_of_contents_imgproc.markdown 颜色空间、几何变换、二值化、滤波、形态学、梯度、Canny、金字塔、轮廓、直方图、傅里叶变换、模板匹配、霍夫、Watershed、GrabCut 等
Video js_table_of_contents_video.markdown Meanshift/Camshift 跟踪、Lucas-Kanade 光流、背景减除
DNN js_table_of_contents_dnn.markdown 图像分类(含摄像头版)、目标检测(含摄像头版)、语义分割、风格迁移、姿态估计

每个模块目录下的 .markdown 对应一个 Doxygen 教程页,配套的交互式 HTML 示例统一放在 js_assets 目录中(例如 js_canny.htmljs_meanshift.htmljs_object_detection.html 等上百个页面),配合公共工具脚本 utils.js 和样式 js_example_style.css,以及 apple.jpgcoins.jpgcup.mp4 等示例素材。

快速上手:在网页中使用预构建的 opencv.js

官方明确提示:如果只是开始使用,不必自己构建,可以直接从发布包 opencv-{VERSION_NUMBER}-docs.zip 或在线文档站获取预构建的 opencv.js,也可以按后文教程自行构建。完整步骤见 Using OpenCV.js,核心流程分四步:

第 1 步:创建能上传图片的网页

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello OpenCV.js</title>
</head>
<body>
<h2>Hello OpenCV.js</h2>
<div>
  <div class="inputoutput">
    <img id="imageSrc" alt="No Image" />
    <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
  </div>
</div>
<script type="text/javascript">
let imgElement = document.getElementById("imageSrc")
let inputElement = document.getElementById("fileInput");
inputElement.addEventListener("change", (e) => {
  imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);
</script>
</body>
</html>

官方建议用本地 Web 服务器来托管 index.html,而不是直接双击打开文件。

第 2 步:引入 OpenCV.js

opencv.js 的 URL 设置到 <script> 标签的 src 属性。同步加载:

<script src="opencv.js" type="text/javascript"></script>

异步加载(推荐,避免阻塞渲染),并通过 onload 回调感知加载完成:

<script async src="opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>

第 3 步:使用 cv 对象

opencv.js 就绪后,通过 cv 对象访问所有 OpenCV 对象与函数。教程中给出了两个关键 API 细节,这是新版 opencv.js 与旧版的重要差异:

  • cv 是 Promise 类型的对象,需要用 await 解包
imgElement.onload = async function() {
  cv = (cv instanceof Promise) ? await cv : cv;
  let mat = cv.imread(imgElement);
}
  • cv.Mat 的内存分配在 Emscripten 堆上,必须手动调用 delete() 释放,否则会持续泄漏内存。

第 4 步:显示 Mat 到 canvas

显示一个 cv.Mat 需要一个 canvas 元素,用 cv.imshow(canvasId, mat) 完成:

<canvas id="outputCanvas"></canvas>
...
cv.imshow("outputCanvas", mat);

教程给出的完整可运行示例把上述步骤串起来,并通过 Module.onRuntimeInitialized 钩子更新加载状态提示(这是 Emscripten 运行时初始化完成时的标准回调):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello OpenCV.js</title>
</head>
<body>
<h2>Hello OpenCV.js</h2>
<p id="status">OpenCV.js is loading...</p>
<div>
  <div class="inputoutput">
    <img id="imageSrc" alt="No Image" />
    <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
  </div>
  <div class="inputoutput">
    <canvas id="canvasOutput" ></canvas>
    <div class="caption">canvasOutput</div>
  </div>
</div>
<script type="text/javascript">
let imgElement = document.getElementById('imageSrc');
let inputElement = document.getElementById('fileInput');
inputElement.addEventListener('change', (e) => {
  imgElement.src = URL.createObjectURL(e.target.files[0]);
}, false);

imgElement.onload = async function() {
  cv = (cv instanceof Promise) ? await cv : cv;
  let mat = cv.imread(imgElement);
  cv.imshow('canvasOutput', mat);
  mat.delete();
};

var Module = {
  // https://emscripten.org/docs/api_reference/module.html#Module.onRuntimeInitialized
  onRuntimeInitialized() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
  }
};
</script>
<script async src="opencv.js" type="text/javascript"></script>
</body>
</html>

从源码构建 opencv.js

完整的构建教程在 Build OpenCV.js,构建入口是仓库中的 build_js.py(配套构建配置脚本为 opencv_js.config.py)。下面按原文档顺序完整继承其全部步骤与参数。

1. 安装 Emscripten

./emsdk update
./emsdk install latest
./emsdk activate latest

安装后确认 EMSDK 环境变量已正确设置:

source ./emsdk_env.sh
echo ${EMSDK}

现代版本的 Emscripten 需要通过 emcmake / emmake 启动器来运行构建命令:

emcmake sh -c 'echo ${EMSCRIPTEN}'

教程标注 emscripten 2.0.10 是被验证支持最新 WebAssembly 特性组合的版本,如需固定版本:

./emsdk update
./emsdk install 2.0.10
./emsdk activate 2.0.10

2. 获取 OpenCV 源码

  • 稳定版本:到 OpenCV 发布页下载源码包并解压;
  • 开发版本:用 Git 克隆 opencv 仓库(需要开发环境装有 git)。

3. 执行构建

构建脚本 build_js.py 的调用格式为 <opencv_src_dir>/platforms/js/build_js.py <build_dir>默认构建 WebAssembly 版本--build_wasm 开关仅为向后兼容保留)。默认行为是把 WebAssembly 代码以 base64 编码打包进单个 JavaScript 文件;生产环境建议追加 --disable_single_file,把 Wasm 写成独立的 .wasm 文件,生成的 JS 会自动加载它,从而减小总体积。

build_js 目录构建:

emcmake python ./opencv/platforms/js/build_js.py build_js

前提条件:开发环境装有 pythoncmake。一个重要的版本相关约束:使用 Emscripten 4.0.20 或更高版本构建时,必须追加 C++17 选项,因为 Embind 自该版本起要求 C++17:

emcmake python ./opencv/platforms/js/build_js.py build_js --cmake_option="-DCMAKE_CXX_STANDARD=17"

4. 可选构建开关一览

开关 命令示例 说明
--build_loader emcmake python ./platforms/js/build_js.py build_js --build_loader 额外构建 OpenCV.js 加载器,输出到 <opencv_js_dir>/bin/loader.js
--build_doc ... build_js --build_doc 构建文档,需要安装 doxygen
--build_test ... build_js --build_test 构建单元测试,生成可直接随 opencv.js 一起运行的测试代码
contrib 模块 ... build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=/path/to/opencv_contrib/modules/" 通过 CMake 变量启用 opencv_contrib 中的扩展模块
--webnn ... build_js --webnn 启用 WebNN 后端(Web 端神经网络推理 API)
--threads ... build_js --build_wasm --threads 构建支持多线程的优化版本
--simd ... build_js --build_wasm --simd 构建启用 WebAssembly SIMD 的版本
--build_wasm_intrin_test ... build_js --build_wasm --simd --build_wasm_intrin_test 构建 wasm 内建函数(intrinsics)测试
--build_perf ... build_js --build_perf 构建性能测试

loader.js 用法:加载器基于 WebAssembly Feature Detection 库检测浏览器能力,自动加载对应的 opencv.js 构建产物。使用 UMD 版本的 feature-detect 库并在应用中引入 loader.js 后,传入路径配置和主函数即可:

// Set paths configuration
let pathsConfig = {
    wasm: "../../build_wasm/opencv.js",
    threads: "../../build_mt/opencv.js",
    simd: "../../build_simd/opencv.js",
    threadsSimd: "../../build_mtSIMD/opencv.js",
}

// Load OpenCV.js and use the pathsConfiguration and main function as the params.
loadOpenCV(pathsConfig, main);

从这段配置可以看出构建产物的四种组合:纯 wasm、多线程、SIMD、多线程+SIMD,loader 按浏览器能力自动择一。

多线程(--threads):默认线程数为设备逻辑核心数;可在运行时用 cv.parallel_pthreads_set_threads_num(number) 设置线程数、cv.parallel_pthreads_get_threads_num() 查询当前值。限制条件:必须是 wasm 版构建;只在浏览器中生效,Node.js 中无效;且浏览器需先启用 "WebAssembly threads support"(Chrome 需在 chrome://flags 中开启对应标志)。

SIMD(--simd):教程标注该项在 wasm simd 仍在演进时属于实验特性。当时只有 emscripten LLVM upstream 后端支持 wasm simd,需要先切换 upstream 工具链:

./emsdk update
./emsdk install latest-upstream
./emsdk activate latest-upstream
source ./emsdk_env.sh

限制条件:浏览器需开启 "WebAssembly SIMD support" 标志;Node.js 需以 --experimental-wasm-simd 启动脚本;且用最新 LLVM upstream 构建的 simd 版本可能无法在稳定版浏览器或旧版 Node.js 上运行,建议使用最新版不稳定版浏览器(如 Chrome Dev)体验新特性。

wasm intrinsics 测试(--build_wasm_intrin_test):构建后可用 cv.test_hal_intrin_all() 跑全部用例,失败用例会输出到 JS 调试控制台;也可按数据类型单独测试,覆盖 uint8/int8/uint16/int16/uint32/int32/uint64/int64/float32/float64 共 10 个类型,对应 cv.test_hal_intrin_uint8()cv.test_hal_intrin_float64() 系列函数。

性能测试(--build_perf):在 <build_dir>/bin 启动本地 Web 服务器后,浏览器访问形如 http://localhost:8080/perf/perf_imgproc/perf_threshold.html 的页面;当前性能测试包含 cvtColorresizethreshold 等内核。页面中可以输入形如 (1920x1080, CV_8UC1, THRESH_BINARY) 的参数只跑单个用例,不输入则跑该内核的全部用例。也可以用 Node.js 运行,例如:

cd bin/perf
npm install
node perf_threshold.js --test_param_filter="(1920x1080, CV_8UC1, THRESH_BINARY)"

5. 用 Docker 构建(推荐在 Linux/macOS/Windows 上的替代方案)

官方指出,用容器执行同一构建往往更简单可靠,尤其在非 Linux 系统上——只需安装 Docker,使用已经预装好全部工具的 emscripten 官方镜像即可。Linux/macOS:

git clone https://github.com/opencv/opencv.git
cd opencv
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk emcmake python3 ./platforms/js/build_js.py build_js

Windows PowerShell:

docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk" emcmake python3 ./platforms/js/build_js.py build_js

如果最新 emscripten 构建失败,教程建议回退到已知可用的 2.0.10 标签镜像(Linux/macOS 与 Windows PowerShell 各一条对应命令,仅镜像 tag 不同)。

用 Docker 构建文档:由于 --build_doc 需要 doxygen,官方给出了最小 Dockerfile(基于 emscripten/emsdk:2.0.10apt-get 安装 doxygen 并清理 apt 缓存),先 docker build . -t opencv-js-doc 构建镜像(只需一次),然后:

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) "opencv-js-doc" emcmake python3 ./platforms/js/build_js.py build_js --build_doc

运行 opencv.js 测试

前提是构建时已传 --build_test,测试代码会生成在 build_js/bin 中。教程给出三种运行方式:

方式一:浏览器中手动运行。<build_dir>/bin 启动本地 Web 服务器(如 node http-server,监听 8080),访问 http://localhost:8080/tests.html 即自动执行单元测试:

npx http-server build_js/bin
firefox http://localhost:8080/tests.html

(以下片段均要求安装 Node.js。)

方式二:Puppeteer 无头运行,适合 CI 场景:

cd build_js/bin
npm install
npm install --no-save puppeteer    # automatically downloads Chromium package
node run_puppeteer.js

node run_puppeteer --help 可查看调试与报告选项。npm install 只需执行一次;可用 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 npm install --no-save puppeteer 跳过 Chromium 自动下载,并通过 PUPPETEER_EXECUTABLE_PATH=$(which google-chrome) 指定自己的 Chrome/Chromium 二进制。官方提醒:Puppeteer 仅在捆绑的 Chromium 上得到保证,自担风险。

方式三:Node.js 直接运行:

cd build_js/bin
npm install
node tests.js

教程备注:如果所有测试都失败,考虑使用 Node.js 8.x 版本(nvm 的 lts/carbon)。

五大模块教程详解

GUI:显示、视频与 trackbar

GUI 模块包含三个教程:

Core:基本操作与数据结构

Core 模块覆盖三块内容:

  • 基本操作js_basic_ops):读写像素值、操作图像 ROI(Region of Interest)等;
  • 图像算术运算js_image_arithmetics):对图像做加、减、按位与或等非算术/算术运算;
  • 常用数据结构:理解 cv.Mat 等核心类型及其在内存管理中的角色(与前述 mat.delete() 呼应)。

ImgProc:图像处理函数全家桶

ImgProc 模块是教程体中最大的部分,每个子主题都有对应的交互 HTML 示例(均在 js_assets 下):

主题 教程 核心内容
颜色空间 js_colorspaces 在不同颜色空间之间转换(cvtColor 等)
几何变换 js_geometric_transformations 缩放、旋转、仿射与透视变换
二值化 js_thresholding 全局阈值、自适应阈值、Otsu 二值化
滤波 js_filtering 模糊、自定义核滤波
形态学 js_morphological_ops 腐蚀、膨胀、开闭运算
梯度 js_gradients 图像梯度与边缘初探
Canny js_canny Canny 边缘检测
金字塔 js_pyramids 图像金字塔与图像混合
轮廓 js_table_of_contents_contours 轮廓查找、层级、轮廓特征(面积、周长、凸包、矩等)
直方图 js_table_of_contents_histograms 直方图计算、均衡化、反向投影
变换 js_table_of_contents_transforms 傅里叶变换、余弦变换
模板匹配 js_template_matching 在图像中搜索目标
霍夫变换 js_houghlines / js_houghcircles 直线检测 / 圆检测
Watershed js_watershed 分水岭图像分割
GrabCut js_grabcut GrabCut 前景提取
摄像头处理 js_imgproc_camera 对视频采集流做实时图像处理
智能剪刀 js_intelligent_scissors 交互式图像分割工具

Canny 教程为例,文档按算法阶段完整讲解原理并落到 cv.Canny() 的调用:① 噪声抑制——先用 5x5 高斯滤波器去噪;② 求强度梯度——水平/垂直方向 Sobel 核滤波得到 GxG_xGyG_y,边缘强度 G=Gx2+Gy2G = \sqrt{G_x^2 + G_y^2},方向 θ=1(Gy/Gx),梯度方向与边缘垂直并被量化为垂直、水平、两条对角四个角度之一;③ 非极大值抑制——沿梯度方向检查每个像素是否为邻域局部极大值,是则保留、否则置零,得到"细边缘"二值图;④ 滞后阈值——用 minVal/maxVal 两个阈值区分"确定边缘""确定非边缘",中间灰度值通过与强边缘的连通性来决定归属。交互示例见 js_canny.html

Video:视频分析

Video 模块包含三个教程:

DNN:深度学习推理

DNN 模块展示如何在 JavaScript 中使用 dnn 模块,五个端到端示例都带有模型信息说明文件:

示例 教程 模型说明
图像分类 js_image_classification js_image_classification_model_info.json
图像分类(摄像头) js_image_classification_with_camera 同上
目标检测 js_object_detection / 摄像头版 js_object_detection_model_info.json
语义分割 js_semantic_segmentation js_semantic_segmentation_model_info.json
风格迁移 js_style_transfer js_style_transfer_model_info.json
姿态估计 js_pose_estimation js_pose_estimation_model_info.json

除标准 dnn 推理外,教程资产中还提供了 WebNN 方向的示例js_image_classification_webnn_polyfill.html(浏览器中使用 WebNN polyfill 做图像分类)以及 webnn-electron 目录下的 Electron 桌面应用(main.jsutils_webnn_electron.jspackage.json),与构建侧的 --webnn 开关相呼应——从源码结构看,构建脚本的 WebNN 支持与这些示例页面共同构成了仓库中 Web 端神经网络推理的完整链路。

在 Node.js 中使用 OpenCV.js

Setup 模块最后一个教程 Using OpenCV.js in Node.js 讲解如何在 Node.js 环境中加载和使用 opencv.js(而非浏览器),这也是前面测试部分"方式三:node tests.js"的基础。

关键路径索引与适用前提

适用前提小结:整套教程以 OpenCV 3.x 为讲解基线,但构建文档中已体现当前仓库支持的演进特性(Emscripten 4.0.20+ 需 C++17、WebNN 后端、多线程/SIMD 构建变体)。实际环境约束包括:threads 优化仅浏览器有效;SIMD 需要相应浏览器标志或 Node.js 实验性开关;Docker 构建在非 Linux 平台上尤为推荐;内存管理必须显式 delete()。遵循"预构建快速上手 → 按需自定义构建 → 按模块深入教程"的路径,即可把 OpenCV.js 完整落地到自己的 Web 视觉应用中。

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