NodeJS get pixel color from another window - javascript

How can you access the color of a pixel on the screen using NodeJS?
My use-case is for Windows, but a cross-platform solution would be ideal.
Here's an example in C# which could be helpful: C# get pixel color from another window

I ended up converting the linked C# answer to NodeJS by using node-ffi:
import ffi from "ffi";
import chroma from "chroma-js";
var user32 = new ffi.Library("user32", {
"GetDC": ["int32", ["int32"]],
"ReleaseDC": ["int32", ["int32", "int32"]],
});
var gdi32 = new ffi.Library("gdi32", {
"GetPixel": ["uint32", ["int32", "int32", "int32"]],
});
export function GetPixelColor(x: number, y: number) {
let hdc = user32.GetDC(0);
let pixel = gdi32.GetPixel(hdc, x, y);
user32.ReleaseDC(0, hdc);
let color = chroma(pixel & 0x000000FF, (pixel & 0x0000FF00) >> 8, (pixel & 0x00FF0000) >> 16);
return color.hex().substr(1); // to match with robotjs.getPixelColor() returns
}
To use:
let pixelColor = GetPixelColor(0, 0);
console.log("Color: #" + pixelColor);
UPDATE
Since then, I put together this library: https://github.com/Venryx/windows-ffi
It supports screenshotting of regions of the screen, rather than just individual pixels. This makes it much faster for reading image-data over an area, and is the solution I use nowadays.
Alternatives
windows-ss: https://github.com/Sxxov/windows-ss
screenshot-desktop: https://github.com/bencevans/screenshot-desktop
desktop-screenshot: https://github.com/johnvmt/node-desktop-screenshot
Note: Does not have a built-in way to access the raw pixel data. (you'd have to read and parse the file manually)
RobotJS: https://github.com/octalmage/robotjs
Warning: It currently causes crashes if you call getPixelColor when a UAC prompt is open. (see issue)

Related

Trying to scrape items in a canvas with selenium

I'm trying to scrape the High, Low ( These will show by going to settings > scale and enabling their lines ) and current price from this element, a canvas (XPATH: "/html/body/div[2]/div[1]/div[2]/div[1]/div[2]/table/tr[1]/td[3]/div/canvas[1]" ) from this webpage with python, using selenium.
I found some answers that were trying to get back to the JavaScript function done to get the values, I tried but without actually finding anything ( I'm not that good at reversing JavaScript ).
Thank you in advance for the help.
Assuming that the text is actually being drawn client side on a canvas object:
You could inject your own canvas ctx by changing the prototype getContext(...) method on HTMLCanvasElement. Adapting the code here:
HTMLCanvasElement.prototype.__oldGetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = (type, options) => {
let ctx = this.__oldGetContext(type, options);
ctx.__oldfillText = ctx.fillText
ctx.fillText = (text, x, y, options) => {
console.log('Drawing text ', text, 'at position ', x, y);
ctx.__oldfillText(text, x, y, options)
}
return ctx;
}
You might additionally have to override strokeText, and the getContext method for the other canvas types.
However, if the text is being rendered server-side, then you're out of luck and have to use OCR. On the other hand, if you just want pricing data, there are loads of crypto (smh) pricing apis out there that would be far less hassle than any of this.

Different predictions if running in Node instead of Browser (using the same model_web - python converted model)

pretty new to ML and tensorflow!
I made an object detection model with http://cloud.annotations.ai that permits to train and convert a model in different formats, tfjs (model_web) too.
That website provides also boilerplates for running the model within a browser (react app)... just like you do - probably it is the same code, didn't spend enough time.
So I have this model running inside a browser, giving prediction about objects in a photo with pretty good results considering the amount of example I gave and the prediction score (0.89). the given bounding box is good too.
But, unfortunately, I didn't have "just one video" to analyze frame by frame inside a browser, I've got plenty of them. So I decided to switch to node.js, porting the code as is.
Guess what? TF.js relies on DOM and browser components, and almost none examples that works with Node exists. So not a big deal, just spent a morning figuring out all the missing parts.
Finally I'm able to run my model over videos that are splitted in frames, at a decent speed - although having the "Hello there, use tfjs-node to gain speed" banner when I'm already using tfjs-node - but results seems odd.
Comparing the same picture with the same model_web folder gave the same prediction but with lower score (0.80 instead of 0.89) and a different bounding box, with object not centered at all.
(TL;DR)
Does tfjs have different implementation of the libraries (tfjs and tfjs-node) that makes different use of the same model? I don't think it can be a problem of input because - after a long search and fight - i figure out two ways to give the image to tf.browser.getPixel in Node (and I'm still wondering why I have to use a "browser" method inside tfjs-node). Anyone made comparisons?
So... that's the code I used, for your reference:
model_web is being loaded with tf.loadGraphModel("file://path/to/model_web/model.json");
two different ways to convert a JPG and make it works with tf.browser.getPixel()
const inkjet = require('inkjet');
const {createCanvas, loadImage} = require('canvas');
const decodeJPGInkjet = (file) => {
return new Promise((rs, rj) => {
fs.readFile(file).then((buffer) => {
inkjet.decode(buffer, (err, decoded) => {
if (err) {
rj(err);
} else {
rs(decoded);
}
});
});
});
};
const decodeJPGCanvas = (file) => {
return loadImage(file).then((image) => {
const canvas = createCanvas(image.width, image.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height);
const data = ctx.getImageData(0, 0, image.width, image.height);
return {data: new Uint8Array(data.data), width: data.width, height: data.height};
});
};
and that's the code that use the loaded model to give predictions - same code for node and browser, found at https://github.com/cloud-annotations/javascript-sdk/blob/master/src/index.js - doesn't works on node as it is, I changed require("#tensorflow/tfjs"); with require("#tensorflow/tfjs-node"); and replaced fetch with fs.read
const runObjectDetectionPrediction = async (graph, labels, input) => {
const batched = tf.tidy(() => {
const img = tf.browser.fromPixels(input);
// Reshape to a single-element batch so we can pass it to executeAsync.
return img.expandDims(0);
});
const height = batched.shape[1];
const width = batched.shape[2];
const result = await graph.executeAsync(batched);
const scores = result[0].dataSync();
const boxes = result[1].dataSync();
// clean the webgl tensors
batched.dispose();
tf.dispose(result);
const [maxScores, classes] = calculateMaxScores(
scores,
result[0].shape[1],
result[0].shape[2]
);
const prevBackend = tf.getBackend();
// run post process in cpu
tf.setBackend("cpu");
const indexTensor = tf.tidy(() => {
const boxes2 = tf.tensor2d(boxes, [result[1].shape[1], result[1].shape[3]]);
return tf.image.nonMaxSuppression(
boxes2,
maxScores,
20, // maxNumBoxes
0.5, // iou_threshold
0.5 // score_threshold
);
});
const indexes = indexTensor.dataSync();
indexTensor.dispose();
// restore previous backend
tf.setBackend(prevBackend);
return buildDetectedObjects(
width,
height,
boxes,
maxScores,
indexes,
classes,
labels
);
};
Do different implementation of the libraries (tfjs and tfjs-node) that makes different use of the same model
If the same model is deployed both in the browser and in nodejs, the prediction will be the same thing.
If the predicted value are different, it might be related to the tensor used for the prediction. The processing from the image to the tensor might be different resulting in different tensors being used for the prediction thus causing the output to be different.
i figure out two ways to give the image to tf.browser.getPixel in Node (and I'm still wondering why I have to use a "browser" method inside tfjs-node)
The canvas package use the system graphic to create the browser like canvas environment that can be used by nodejs. This makes it possible to use tf.browser namespace especially when dealing with image conversion. However it is still possible to use directly nodejs buffer to create a tensor.

Get the pixel screen size in Spark AR studio (for Facebook)

I am starting to work with Spark AR studio and I looking for to get the screen size in pixel to compare the coordinate obtained by the gesture.location on Tap.
TouchGestures.onTap().subscribe((gesture) => {
// ! The location is always specified in the screen coordinates
Diagnostics.log(`Screen touch in pixel = { x:${gesture.location.x}, y: ${gesture.location.y} }`);
// ????
});
The gesture.location is in pixel (screen coordinate) and would like to compare it with the screen size to determine which side of the screen is touched.
Maybe using the Camera.focalPlane could be a good idea...
Update
I tried two new things to have the screen size:
const CameraInfo = require('CameraInfo');
Diagnostics.log(CameraInfo.previewSize.height.pinLastValue());
const focalPlane = Scene.root.find('Camera').focalPlane;
Diagnostics.log(focalPlane.height.pinLastValue());
But both return 0
This answer might be a bit late but it might be a nice addition for people looking for a solution where the values can easily be used in script, I came across this code(not mine, forgot to save a link):
var screen_height = 0;
Scene.root.find('screenCanvas').bounds.height.monitor({fireOnInitialValue: true}).subscribe(function (height) {
screen_height = height.newValue;
});
var screen_width = 0;
Scene.root.find('screenCanvas').bounds.width.monitor({fireOnInitialValue: true}).subscribe(function (width) {
screen_width = width.newValue;
});
This worked well for me since I couldn't figure out how to use Diagnostics.log with the data instead of Diagnostics.watch.
Finally,
Using the Device Info in the Patch Editor and passing these to the script works!
First, add a variable "to script" in the editor:
Then, create that in patch editor:
And you can grab that with this script:
const Patches = require('Patches');
const screenSize = Patches.getPoint2DValue('screenSize');
My mistake was to use Diagnostic.log() to check if my variable worked well.
Instead use Diagnostic.watch():
Diagnostic.watch('screenSize.x', screenSize.x);
Diagnostic.watch('screenSize.y', screenSize.y);
Screen size is available via the Device Info patch output, after dragging it to patch editor from the Scene section.
Now in the open beta (as of this post) you can drag Device from the scene sidebar into the patch editor to get a patch that outputs screen size, screen scale, and safe area inserts as well as the self Object.
The Device patch
The device size can be used in scripts using CameraInfo.previewSize.width and CameraInfo.previewSize.height respectively. For instance, if you wanted to get 2d points representing the min/max points on the screen, this'd do the trick.
const CameraInfo = require('CameraInfo')
const Reactive = require('Reactive')
const min = Reactive.point2d(
Reactive.val(0),
Reactive.val(0)
)
const max = Reactive.point2d(
CameraInfo.previewSize.width,
CameraInfo.previewSize.height
)
(The point I want to emphasize being that CameraInfo.previewSize.width and CameraInfo.previewSize.height are ScalarSignals, not number literals.)
Edit: Here's a link to the documentation: https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/camerainfomodule

Emscripten - surfData.colors32 is undefined, SDL_FillRect

I'm using Emscripten to try to get an open source game to run in a browser. It compiles fine, loads all of its files any everything, but when I run it it get the following exception:
exception thrown: TypeError: surfData.colors32 is undefined,_SDL_FillRect#file:///home/misson20000/dev/js/game.js:9702:9
__ZN9Surface5ClearEhhh#file:///home/misson20000/dev/js/game.js:112026:3
...
_main#file:///home/misson20000/dev/js/game.js:10525:11
asm._main#file:///home/misson20000/dev/js/game.js:170793:10
callMain#file:///home/misson20000/dev/js/game.js:173065:15
doRun#file:///home/misson20000/dev/js/game.js:173122:7
run/<#file:///home/misson20000/dev/js/game.js:173134:7
The code that is calling SDL_FillRect (a simple clear function) follows:
SDL_FillRect(fSurface, NULL, MapColor(r, g, b));
MapColor is defined as
return SDL_MapRGB(fSurface->format, r, g, b);
Digging around in the source code for a bit reveals that the surface in question is a screen surface.
How can I made surfData.colors32 not be undefined?
The colors32 is used when you create SDL surface with SDL_HWPALETTE flag. To correctly use surface of this type you should call SDL_SetColors before SDL_FillRect. Take a look in src/library_sdl.js:
SDL_SetColors: function(surf, colors, firstColor, nColors) {
var surfData = SDL.surfaces[surf];
// we should create colors array
// only once cause client code
// often wants to change portion
// of palette not all palette.
if (!surfData.colors) {
var buffer = new ArrayBuffer(256 * 4); // RGBA, A is unused, but faster this way
surfData.colors = new Uint8Array(buffer);
surfData.colors32 = new Uint32Array(buffer);
}
//...
}

Why cocos2d-js + chipmunk, only works with web build

I've implemented simple example based on this
My example uses chipmunk along with cocos2d-js.
The problem is that physic only works with web builds. With the other builds (native ones - ios, mac, win32) all object are shown but they just hang - no animation.
My update method is called with specified intervals, where I execute "step" method on space object.
All my sprites are loaded using PhysicSprite class.
PS: I'm using cocos2d-js v3.0alpha
Use this tutorial:
http://www.cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter6/en
I tried it both in the browser and in the iphone simulator and it worked just fine.
you should apply impulse on your physics body, they will move surely but if you would try to move the body with schedular by changing its coordinate on every call they will work on web but not on native ones like iOS or mac .
for example:-
var mass = 1;
var width = 1, height = 1;
playerBody = new cp.Body(mass , cp.momentForBox(mass, width, height));
playerBody.applyImpulse(cp.v(200, 300), cp.v(0, 0));// now you can move your playerBody
it will work well on all the platform but if you try my alternate solution
ie:-
init: function{
var mass = 1;
var width = 1, height = 1;
this.playerBody = new cp.Body(mass , cp.momentForBox(mass, width, height));
this.schedule(this.move);
},
move: function(dt){
this.playerBody.getPos().x += 2 * dt;
this.playerBody.getPos().y += 2 * dt;
}
this will work on web but on native platform like iOS or mac it will not move the playerBody at all. i don't know the reason yet if i got one i will let you know

Categories