opencv 支持编译多个平台,其中还支持JavaScript,不过编译需要emscripten
编译环境:centos7,Python2.7
1.下载OpenCV源码
官网:https://opencv.org/releases/
例如下载4.8.0版本:
https://github.com/opencv/opencv/archive/4.8.0.zip
2.利用镜像 trzeci/emscripten 构建
#解压OpenCV
unzip opencv-4.8.0.zip
#进入opencv-4.8.0
cd opencv-4.8.0
#拉最新的trzeci/emscripten
docker pull trzeci/emscripten
#开始编译
docker run --rm --workdir /code -v "$PWD":/code "trzeci/emscripten" python ./platforms/js/build_js.py buildjs
最后编译结果都放在buildjs,其中opencv.js 在 buildjs/bin 下面,拷贝出来就可以用了
当然,也可以直接线上已经编译好的:
https://docs.opencv.org/4.8.0/opencv.js
附上 nodejs 示例
js
const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas');
const { JSDOM } = require('jsdom');
const { writeFileSync, existsSync, mkdirSync } = require('fs');
(async () => {
await loadOpenCV();
await detect('../out/imgs/lena.jpg')
})();
const detect = async(imgPath) => {
console.time(imgPath)
const image = await loadImage(imgPath);
const src = cv.imread(image);
let gray = new cv.Mat();
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();
// Load pre-trained classifier files. Notice how we reference local files using relative paths just
// like we normally would do
faceCascade.load('./models/haarcascade_frontalface_alt2.xml');
eyeCascade.load('./models/haarcascade_eye.xml');
let mSize = new cv.Size(100, 100);
faceCascade.detectMultiScale(gray, faces, 1.11, 6, 0, mSize);
console.timeEnd(imgPath)
console.log('face size: ',faces.size())
for(let i=0;i<faces.size();i++) {
console.log(faces.get(i))
}
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);
console.log(eyes.size(),'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(imgPath+'-output.jpg', canvas.toBuffer('image/jpeg'));
src.delete(); gray.delete(); faceCascade.delete();
eyeCascade.delete();
faces.delete();
eyes.delete()
}
/**
* Loads opencv.js.
*
* Installs HTML Canvas emulation to support `cv.imread()` and `cv.imshow`
*
* Mounts given local folder `localRootDir` in emscripten filesystem folder `rootDir`. By default it will mount the local current directory in emscripten `/work` directory. This means that `/work/foo.txt` will be resolved to the local file `./foo.txt`
* @param {string} rootDir The directory in emscripten filesystem in which the local filesystem will be mount.
* @param {string} localRootDir The local directory to mount in emscripten filesystem.
* @returns {Promise} resolved when the library is ready to use.
*/
function loadOpenCV (rootDir = './work', localRootDir = process.cwd()) {
if (global.Module && global.Module.onRuntimeInitialized && global.cv && global.cv.imread) {
return Promise.resolve()
}
return new Promise(resolve => {
installDOM()
global.Module = {
onRuntimeInitialized () {
// We change emscripten current work directory to 'rootDir' so relative paths are resolved
// relative to the current local folder, as expected
cv.FS.chdir(rootDir)
resolve()
},
preRun () {
// preRun() is another callback like onRuntimeInitialized() but is called just before the
// library code runs. Here we mount a local folder in emscripten filesystem and we want to
// do this before the library is executed so the filesystem is accessible from the start
const FS = global.Module.FS
// create rootDir if it doesn't exists
if (!FS.analyzePath(rootDir).exists) {
FS.mkdir(rootDir);
}
// create localRootFolder if it doesn't exists
if (!existsSync(localRootDir)) {
mkdirSync(localRootDir, { recursive: true });
}
// FS.mount() is similar to Linux/POSIX mount operation. It basically mounts an external
// filesystem with given format, in given current filesystem directory.
FS.mount(FS.filesystems.NODEFS, { root: localRootDir }, rootDir);
}
};
global.cv = require('./opencv.js')
});
}
function installDOM () {
const dom = new JSDOM();
global.document = dom.window.document;
global.Image = Image;
global.HTMLCanvasElement = Canvas;
global.ImageData = ImageData;
global.HTMLImageElement = Image;
}