'Is is possible to use OpenCV.js with Web Worker in Angular?
I'm trying to use OpenCV.js in web workers in Angular. My project needs to conduct multiple graphic computings at the same time, which would take long time.
But when I use OpenCV in a *.worker.ts file like components, it said function not defined.
blur.worker.ts:
/// <reference lib="webworker" />
addEventListener('message', ({ data }) => {
const src = data.src;
const color = data.color;
const kSize = data.kSize;
if (color === false) {
cv.cvtColor(src, src, cv.COLOR_RGB2GRAY, 0);
}
const dst = new cv.Mat();
if (kSize > 0) {
cv.medianBlur(src, dst, kSize);
} else {
src.copyTo(dst);
}
postMessage({dst});
});
Main thread (a service):
const src = cv.imread(srcImg.nativeElement.id);
const worker = new Worker('../workers/blur.worker.ts', { type: 'module' });
worker.onmessage = ({ data }) => {
cv.imshow(dstImg.nativeElement.id, data.dst);
};
worker.postMessage({src, color, args});
It seems opencv.js has been loaded, but web worker cannot access it.
Solution 1:[1]
You can fix by removing this code block from generated opencv.js file, present at the top of the file. 'cv' object will be returned as promise.
else if (typeof importScripts === 'function') {
// Web worker
root.cv = factory;
}
Solution 2:[2]
This took me a stupidly long time to figure out. But here you go everyone.
addEventListener('message', async (data) => {
self["Module"] = {
scriptUrl: '/assets/opencv/opencv.js',
wasmBinaryFile: '/assets/opencv/opencv_js.wasm',
usingWasm: true,
onRuntimeInitialized: () => { }
}
//Hack to fake correct path to .wasm file
let scriptDirectory = self.location.href
scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf("/")) + self["Module"].wasmBinaryFile;
let document = { currentScript: { src: scriptDirectory } }
self["document"] = document;
//Load and await the .js OpenCV
self.importScripts(self["Module"].scriptUrl);
cv = await cv;
//!!Use cv here!!
postMessage(response);
});
Normaly when loading OpenCV, the Module infomation is declared globlay using window["Module"]
, however as we are a web worker, we don't have acess to window. Instead i replaced this with self["Module"]
.
The hack using scriptDirectory
is designed to take advantage of the _scriptDir
variable (in opencv.js) to overide the nasty scriptDirectory
generated in the ENVIROMENT_IS_WORKER
if else block.
Have fun!
for Typescript replace
self["document"] = document;
self.importScripts(self["Module"].scriptUrl);
with
//@ts-ignore
self["document"] = document;
//@ts-ignore
self.importScripts(self["Module"].scriptUrl);
(Do bear in mind that this will load opencv from the server every time the worker is pinnged, consider moving this code outside of the onmessage)
Solution 3:[3]
For guys looking for importing OpenCV in module workers, i.e.
new Worker('worker.js', { type: 'module' })
I've come up with the following solution.
Download "opencv-4.5.5.js" from OpenCV official site https://docs.opencv.org/4.x/d0/d84/tutorial_js_usage.html
Change this file (1): Add
let Module;
to the first line, so that line 44-46 will not give you error.
// line 44-46
if (typeof Module === 'undefined')
Module = {};
return cv(Module);
- Change this file (2):
As you will find
root
be undefined, and it comes from line 22. Make a fallback toself
which we use for workers.
} else {
// Other shells, e.g. d8
root.cv = factory();
}
}(this||self, function () { // <-- original: }(this, function () {
var cv = (function() {
- Change this file (3): Find this and give it a missing ()
} else if (typeof importScripts === 'function') {
// Web worker
root.cv = factory(); // original: root.cv = factory;
}
- I created a wrapper for the import,
opencv-wrapper.js
await import("/lib/opencv-4.5.5.js"); // where your modified file locate
export default cv;
- Import as normal
import cv from "/lib/opencv-wrapper.js"; // where you place your wrapper
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | mAc |
Solution 2 | Peter Csala |
Solution 3 |