OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
在 Node.js 中使用 OpenCV.js

目标

在本教程中,您将学习

注意
除了给出在 Node.js 中运行 OpenCV.js 的说明外,本教程的另一个目标是向用户介绍 emscripten API 的基础知识,例如 Module文件系统,以及 Node.js。

最小示例

创建一个文件 example1.js,内容如下

// 定义一个全局变量 'Module',其中包含一个方法 'onRuntimeInitialized'
Module = {
onRuntimeInitialized() {
// 这是我们的应用程序
console.log(cv.getBuildInformation())
}
}
// 加载 'opencv.js',将值赋给全局变量 'cv'
cv = require('./opencv.js')

执行它

  • 将文件另存为 example1.js
  • 确保文件 opencv.js 位于同一文件夹中。
  • 确保您的系统上已安装 Node.js

以下命令应打印 OpenCV 构建信息

node example1.js

刚刚发生了什么?

  • 在第一个语句中:通过定义一个名为 'Module' 的全局变量,当库准备好使用时,emscripten 将调用 Module.onRuntimeInitialized()。我们的程序位于该方法中,并像在浏览器中一样使用全局变量 cv
  • 语句 "cv = require('./opencv.js')" 需要文件 opencv.js 并将返回值赋给全局变量 cvrequire() 是一个 Node.js API,用于加载模块和文件。在本例中,我们从当前文件夹加载文件 opencv.js,并且如前所述,当 emscripten 准备就绪时,它将调用 Module.onRuntimeInitialized()
  • 有关更多详细信息,请参阅 emscripten Module API

处理图像

OpenCV.js 不支持图像格式,因此我们无法直接加载 png 或 jpeg 图像。在浏览器中,它使用 HTML DOM(例如 HTMLCanvasElement 和 HTMLImageElement 来解码和解码图像)。在 node.js 中,我们需要使用一个库来实现这一点。

在本例中,我们使用 jimp,它支持常见的图像格式并且非常易于使用。

示例设置

执行以下命令以创建一个新的 node.js 包并安装 jimp 依赖项

mkdir project1
cd project1
npm init -y
npm install jimp

示例

const Jimp = require('jimp');
async function onRuntimeInitialized(){
// 使用 jimp 加载本地图像文件。它支持 jpg、png、bmp、tiff 和 gif
var jimpSrc = await Jimp.read('./lena.jpg');
// `jimpImage.bitmap` 属性具有解码后的 ImageData,我们可以用它来创建 cv:Mat
var src = cv.matFromImageData(jimpSrc.bitmap);
// 以下几行是 opencv.js dilate 教程的复制和粘贴
let dst = new cv.Mat();
let M = cv.Mat.ones(5, 5, cv.CV_8U);
let anchor = new cv.Point(-1, -1);
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
// 现在我们完成了,我们想将 `dst` 写入文件 `output.png`。为此,我们创建了一个 `Jimp`
// 图像,它接受图像数据作为 [`Buffer`](https://node.org.cn/docs/latest-v10.x/api/buffer.html)。
// `write('output.png')` 会将其写入磁盘,Jimp 会从给定的文件名推断出输出格式
new Jimp({
width: dst.cols,
height: dst.rows,
data: Buffer.from(dst.data)
})
.write('output.png');
src.delete();
dst.delete();
}
// 最后,像以前一样加载 open.js。函数 `onRuntimeInitialized` 包含我们的程序。
Module = {
onRuntimeInitialized
};
cv = require('./opencv.js');

执行它

  • 将文件另存为 exampleNodeJimp.js
  • 确保示例图像 lena.jpg 存在于当前目录中。

以下命令应生成文件 output.png

node exampleNodeJimp.js

模拟 HTML DOM 和 canvas

您可能已经看到,其余示例使用诸如 cv.imread()cv.imshow() 之类的函数来读取和写入图像。不幸的是,正如前面提到的,由于没有 HTML DOM,它们无法在 Node.js 上运行。

在本节中,您将学习如何使用 jsdomnode-canvas 来在 Node.js 上模拟 HTML DOM,以便这些函数可以工作。

示例设置

与之前一样,我们创建一个 Node.js 项目并安装所需的依赖项

mkdir project2
cd project2
npm init -y
npm install canvas jsdom

示例

const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas');
const { JSDOM } = require('jsdom');
const { writeFileSync, existsSync, mkdirSync } = require("fs");
// 这是我们的程序。这次我们使用 JavaScript async / await 和 promises 来处理异步。
(async () => {
// 在加载 opencv.js 之前,我们模拟一个最小的 HTML DOM。请参见下面的函数声明。
installDOM();
await loadOpenCV();
// 使用 node-canvas,我们将图像文件转换为与 HTML DOM Image 兼容的对象,因此与 cv.imread() 兼容
const image = await loadImage('./lena.jpg');
const src = cv.imread(image);
const dst = new cv.Mat();
const M = cv.Mat.ones(5, 5, cv.CV_8U);
const anchor = new cv.Point(-1, -1);
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
// 我们创建一个与 HTMLCanvasElement 兼容的对象
const canvas = createCanvas(300, 300);
cv.imshow(canvas, dst);
writeFileSync('output.jpg', canvas.toBuffer('image/jpeg'));
src.delete();
dst.delete();
})();
// 像之前一样加载 opencv.js,但使用 Promise 而不是回调
function loadOpenCV() {
return new Promise(resolve => {
global.Module = {
onRuntimeInitialized: resolve
};
global.cv = require('./opencv.js');
});
}
// 使用 jsdom 和 node-canvas,我们定义了一些全局变量来模拟 HTML DOM。
// 虽然可以存档完整的模拟,但这里我们只定义 cv.imread() 和 cv.imshow() 使用的全局变量。
function installDOM() {
const dom = new JSDOM();
global.document = dom.window.document;
// 其余部分启用 DOM 图像和 canvas,由 node-canvas 提供
global.Image = Image;
global.HTMLCanvasElement = Canvas;
global.ImageData = ImageData;
global.HTMLImageElement = Image;
执行它
}

将文件另存为 exampleNodeCanvas.js

  • 以下命令应生成文件 output.jpg
  • 确保示例图像 lena.jpg 存在于当前目录中。

node exampleNodeCanvas.js

处理文件

在本教程中,您将学习如何配置 emscripten,以便它使用本地文件系统进行文件操作,而不是使用内存。此外,它还尝试描述 emscripten 应用程序如何支持文件

访问 emscripten 文件系统在 OpenCV 应用程序中经常需要,例如加载机器学习模型,例如 加载 Caffe 框架模型如何在浏览器中运行深度网络 中使用的模型。

示例设置

在示例之前,值得首先考虑如何在 emscripten 应用程序(例如 OpenCV.js)中处理文件。请记住,OpenCV 库是用 C++ 编写的,而文件 opencv.js 只是通过 emscripten C++ 编译器将该 C++ 代码翻译成 JavaScript 或 WebAssembly。

这些 C++ 源使用标准 API 访问文件系统,并且该实现通常最终会出现在系统调用中,该调用读取硬盘驱动器中的文件。由于浏览器中的 JavaScript 应用程序无法访问本地文件系统,因此 emscripten 会模拟一个标准文件系统,以便编译后的 C++ 代码可以直接使用。

在浏览器中,此文件系统在内存中模拟,而在 Node.js 中,也可以直接使用本地文件系统。这通常是更可取的,因为无需在内存中复制文件的内容。本节介绍了如何做到这一点,即配置 emscripten,以便直接从我们的本地文件系统访问文件,并且相对路径匹配相对于当前本地目录的文件,如预期的那样。

示例

以下是 使用 Haar 级联进行人脸检测 的改编。

const { writeFileSync, existsSync, mkdirSync } = require('fs');

const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas');
const { JSDOM } = require('jsdom');
const image = await loadImage('lena.jpg');
(async () => {
await loadOpenCV();
let gray = new cv.Mat();
const src = cv.imread(image);
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);
let faces = new cv.RectVector();
let eyes = new cv.RectVector();
let faceCascade = new cv.CascadeClassifier();
let eyeCascade = new cv.CascadeClassifier();
// 加载预训练的分类器文件。请注意我们如何使用相对路径引用本地文件
// 就像我们通常会做的那样
faceCascade.load('./haarcascade_frontalface_default.xml');
eyeCascade.load('./haarcascade_eye.xml');
let mSize = new cv.Size(0, 0);
faceCascade.detectMultiScale(gray, faces, 1.1, 3, 0, mSize, mSize);
for (let i = 0; i < faces.size(); ++i) {
let roiGray = gray.roi(faces.get(i));
let roiSrc = src.roi(faces.get(i));
let point1 = new cv.Point(faces.get(i).x, faces.get(i).y);
let point2 = new cv.Point(faces.get(i).x + faces.get(i).width, faces.get(i).y + faces.get(i).height);
cv.rectangle(src, point1, point2, [255, 0, 0, 255]);
eyeCascade.detectMultiScale(roiGray, eyes);
for (let j = 0; j < eyes.size(); ++j) {
let point1 = new cv.Point(eyes.get(j).x, eyes.get(j).y);
let point2 = new cv.Point(eyes.get(j).x + eyes.get(j).width, eyes.get(j).y + eyes.get(j).height);
cv.rectangle(roiSrc, point1, point2, [0, 0, 255, 255]);
roiGray.delete();
}
roiSrc.delete();
const canvas = createCanvas(image.width, image.height);
}
cv.imshow(canvas, src);
writeFileSync('output3.jpg', canvas.toBuffer('image/jpeg'));
src.delete(); gray.delete(); faceCascade.delete(); eyeCascade.delete(); faces.delete(); eyes.delete()
* 加载 opencv.js。
})();
/**
* 安装 HTML Canvas 模拟以支持 `cv.imread()` 和 `cv.imshow`
*
* 将给定的本地文件夹 `localRootDir` 挂载在 emscripten 文件系统文件夹 `rootDir` 中。默认情况下,它会将本地当前目录挂载在 emscripten `/work` 目录中。这意味着 `/work/foo.txt` 将解析为本地文件 `./foo.txt`
*
* @param {string} rootDir emscripten 文件系统中的目录,本地文件系统将挂载在该目录中。
* @param {string} localRootDir 要挂载在 emscripten 文件系统中的本地目录。
* @returns {Promise} 当库准备好使用时解析。
function loadOpenCV(rootDir = '/work', localRootDir = process.cwd()) {
*/
if(global.Module && global.Module.onRuntimeInitialized && global.cv && global.cv.imread) {
return Promise.resolve()
installDOM()
}
return new Promise(resolve => {
// 我们将 emscripten 当前工作目录更改为 'rootDir',因此相对路径将解析
global.Module = {
onRuntimeInitialized() {
// 相对于当前的本地文件夹,如预期的那样
cv.FS.chdir(rootDir)
resolve()
preRun() {
},
// preRun() 是另一个回调,类似于 onRuntimeInitialized(),但在
// 库代码运行之前调用。在这里,我们将本地文件夹挂载在 emscripten 文件系统中,并且我们希望
// 在库执行之前执行此操作,以便可以从一开始就访问文件系统
const FS = global.Module.FS
// 如果 rootDir 不存在则创建它
if(!FS.analyzePath(rootDir).exists) {
FS.mkdir(rootDir);
// 如果 localRootFolder 不存在则创建它
}
if(!existsSync(localRootDir)) {
mkdirSync(localRootDir, { recursive: true});
// FS.mount() 类似于 Linux/POSIX 挂载操作。它基本上挂载一个外部
}
// 文件系统,使用给定的格式,在给定的当前文件系统目录中。
FS.mount(FS.filesystems.NODEFS, { root: localRootDir}, rootDir);
global.cv = require('./opencv.js')
}
};
function installDOM(){
});
}
执行它
global.document = dom.window.document;
// 其余部分启用 DOM 图像和 canvas,由 node-canvas 提供
global.HTMLCanvasElement = Canvas;
global.ImageData = ImageData;
global.HTMLImageElement = Image;
执行它
}

将文件另存为 exampleNodeCanvasData.js

  • 确保文件 aarcascade_frontalface_default.xmlhaarcascade_eye.xml␛ 存在于项目的目录中。它们可以从 OpenCV 来源 获得。
  • 确保示例图像文件 lena.jpg 存在于项目的目录中。它应该显示人脸,以便此示例有意义。已知以下图像有效
  • 以下命令应生成文件 output3.jpg
image

node exampleNodeCanvasData.js

OpenCV 的生成时间为周四 2025 年 7 月 3 日 12:14:36,作者为 doxygen 1.12.0