justadudewhohacks / tfjs-image-recognition-base

A shared codebase for face-api.js and tfjs-tiny-yolov2.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

'Illegal Constructor' exception thrown when used in Electron renderer process

andrewwalters opened this issue · comments

When using this as part of the face-api.js package, I get an 'Illegal Constructor' exception when trying to do a face detection in the renderer process of an Electron app (e.g. faceapi.detectSingleFace()). Looks like face detection is attempting to create a canvas element by calling new HTMLCanvasElement() instead of the required document.createElement('canvas').

The root of the issue appears to be in src/env/initialize.ts, where function createCanvasElement attempts to create a new HTMLCanvasElement directly.

I'm able to work around it by overriding createCanvasElements (and createImageElement), e.g.

const faceapi = require('face-api.js');

faceapi.env.monkeyPatch({
    createCanvasElement: () => document.createElement('canvas'),
    createImageElement: () => document.createElement('img')
});

I see the same issue is filed here: justadudewhohacks/face-api.js#157

Yes, I think we should definitely export the initializer funcitons from here. This way the user can simply fix the environment himself in case his working enivronment is an unusual deviation of the nodejs or browser enivronment.

As for the electron renderer thread it would be optimal to fix the isNodejs() check, such that it doesn't break in the standard nodejs environment. Maybe checking for the browser environment first in initializeEnvironment would already fix this for electron.

What do you think?

I think giving the user the option to specify environment would be best, essentially what monkeyPatch does but in a less seemingly-hackish way. :)

I took a look at the environment setup in my electron app. isBrowser would return true since all of the browser elements are present. But initializeBrowserEnv doesn't give the option having readFile available, which would be present in electron apps as well.

Hello @andrewwalters and @justadudewhohacks
I am also using electron but in my case isNodejs returns true and isBrowser also returns true.

I get the following error when I call detectAllFaces:
Error: pixels passed to tf.fromPixels() must be either an HTMLVideoElement, HTMLImageElement, HTMLCanvasElement or ImageData, but was Canvas

I think the renderer process environment should now get initialized correctly, if you want to give face-api.js v0.16.2 a try.

I also exported createBrowserEnv and createNodejsEnv, so in case there are still issues with incorrect initialization one can easily fix this by faceapi.env.setEnv(faceapi.env.createBrowserEnv()) for example.

Regarding readFile in the renderer process, as far as I am aware, you have to require fs via electron remote in the renderer process right? I also exported a createFileSystem helper, which lets you monkeyPatch all the fs related envs as follows:

const fs = remote.require('fs')
faceapi.env.monkeyPatch(faceapi.env.createFileSystem(fs))

The electron renderer process can access node modules directly without going through remote (unless you've explicitly turned off node integration in your renderer process, in which case I believe you lose access to remote as well). I didn't try the above but instead just explicitly initialized the browser+filesystem environment, and it worked:

const fs = require('fs');
faceapi.env.setEnv(Object.assign(faceapi.env.createBrowserEnv(), faceapi.env.createFileSystem(fs)));

Interesting, ok.

The electron renderer process can access node modules directly without going through remote (unless you've explicitly turned off node integration in your renderer process, in which case I believe you lose access to remote as well). I didn't try the above but instead just explicitly initialized the browser+filesystem environment, and it worked:

const fs = require('fs');
faceapi.env.setEnv(Object.assign(faceapi.env.createBrowserEnv(), faceapi.env.createFileSystem(fs)));

This worked ! Awesome. Thanks ! :)