1

I am trying to use the posenet MobileNetV1 network in an electron app. I want to be able to read image from file system (it does not matter if it is a png or jpg), and run it through the network.

What have I done so far:

I am using the following modules:

import * as posenet from '@tensorflow-models/posenet';
var imageToBase64 = require('image-to-base64');
var toUint8Array = require('base64-to-uint8array')

And initializing the network with:

var net = posenet.load();

In order to read image I am converting it to base64 than to Uint8Array, than I am using them to create an object {data: bytes, width: width, height: height}, which is fitting in the definition of ImageData.

Everything is running but the results in percentages are very low:

{
  score: 0.002851587634615819,
  keypoints: [
    { score: 0.0007664567674510181, part: 'nose', position: [Object] },
    {
      score: 0.0010295170359313488,
      part: 'leftEye',
      position: [Object]
    },
    {
      score: 0.0006740405224263668,
      part: 'rightEye',
      position: [Object]
    },

Notice that in the future I intend to build this app so modules like Canvas are no good since it does not build well.

If someone could give me a working poc it would be great since I am working on that for a very long time.

Yonlif
  • 1,770
  • 1
  • 13
  • 31

2 Answers2

1

electron has two separated contexts; one that can be considered as a server side context called the main context and the renderer context in which the browser and its scripts are called. Though the question is not precise enough, it is trying to execute posenet in the main context of electron which can be compared as if one is trying to run this code in nodejs

posenet in the main renderer

const data = Buffer.from(base64str, 'base64')
const t = tf.node.decodeImage(data)
const net = await posenet.load()
const poses = net.estimateMultiplePoses(t, {
      flipHorizontal: false,
      maxDetections: 2,
      scoreThreshold: 0.6,
      nmsRadius: 20})
  })
  // do whatever with the poses

posenet from a script executed by the browser

const im = new Image()
im.src = base64str
const net = await posenet.load()
im.onload = async() => {
 const poses = await net.estimateMultiplePoses(im, {
      flipHorizontal: false,
      maxDetections: 2,
      scoreThreshold: 0.6,
      nmsRadius: 20})
  })
  // do whatever with the poses
}
edkeveked
  • 17,989
  • 10
  • 55
  • 93
  • Thanks I will try it out, in the base64 should I remove the first 23 chars (`data:image/jpeg;base64,`)? – Yonlif May 22 '20 at 15:50
  • This is still not fixing the low percentages, could you please share everything? tfjs and tfjs-node versions, what image are you loading (.png? .jpg?), with what library have you created the buffer? I am sorry if this is annoying I simply want it to work and I have no idea what is the part that is not failing... – Yonlif May 22 '20 at 19:30
  • Buffer is a native object in nodejs. tf.node is part of `@tensorflow/tfjs-node` package. I am not getting it. What do you mean by `not fixing the low percentages` ? You need to remove the first 23 chars – edkeveked May 22 '20 at 20:16
  • Yes I removed and it didn't solve it, something is just not right when you are using posenet in server side context of electron, so the solution is to move the implementation to render context. Sorry... – Yonlif May 22 '20 at 22:15
  • 1
    See my edited answer, the decoded image should be used in the main context for the prediction – edkeveked May 23 '20 at 06:28
  • I approved the answer but actually you do not need `tfjs-node` and you can handle without it. – Yonlif May 23 '20 at 12:05
  • If you want to use `posenet` on the server side, you will either use tfjs-node or another library to get the image typed array from the base64. On the browser side, you don't need it. The answer made the difference between the 2 cases – edkeveked May 23 '20 at 15:01
0

Even though you copied the structure, maybe PoseNet is checking the if the Object is of a certain class which it will not be unless you actually create ImageData object and then set the fields. That is my guess as to why it doesn't like it.

Have you tried:

let clamped = Uint8ClampedArray.from(someframeBuffer);
let imageData = new ImageData(clamped, width, height);

It seems PoseNet accepts ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement objects that can be passed to its prediction function.

Jason Mayes
  • 301
  • 2
  • 8
  • `ReferenceError: ImageData is not defined`, I do not know how to use any of these elements in electron, the closest thing I can do is replicate the ImageData structure. – Yonlif May 21 '20 at 10:19
  • If I am passing a `Uint8ClampedArray` in my format I am getting `Error: pixels passed to tf.browser.fromPixels() must be either an HTMLVideoElement, HTMLImageElement, HTMLCanvasElement, ImageData in browser, or OffscreenCanvas, ImageData in webworker or {data: Uint32Array, width: number, height: number}, but was Object` which is also very weird (I have tried Uint32Array and it also gives the same error)... – Yonlif May 21 '20 at 10:34
  • Yes so it seems it must be an instance of one of these supported class types to work. What is the issue with using OffscreenCanvas or Canvas? I am not so familiar with Electron. – Jason Mayes May 22 '20 at 00:58
  • Well it is just a no go when you want to move from dev to prod... – Yonlif May 22 '20 at 15:46
  • I was just curious to know why though - I am not familiar with building in Electron environment? Is the canvas implementation in Electron bad or something? This guy seems to use it with a patch: https://medium.com/@andreas.schallwig/do-not-laugh-a-simple-ai-powered-game-3e22ad0f8166 – Jason Mayes May 22 '20 at 20:22
  • No, I don't think that electron has a dedicated canvas implementation. The OP needs to know that electron has two separated contexts; one that can be considered as a server side context called the main context and the renderer context in which the browser and its scripts are called. Though the question is not precise enough, I think that the OP is trying to execute posenet in the main context of electron which can be compared as if one is trying to run this code in nodejs for someone not familiar with electron – edkeveked May 22 '20 at 21:00
  • Yes thanks @edkeveked this is exactly what I did at the end, moved from the server context to the render context of my app, worked like a charm. Make this an answer - it is important for future users to know. – Yonlif May 22 '20 at 22:14