I have ZigJS running in the browser and everything is working well, but I want to record the Kinect webcam images in order to play them back as a recorded video. I've looked through the documentation at http://zigfu.com/apidoc/ but cannot find anything related to the RGB information.
However, this SO answer leads me to believe this is possible:
We also support serialization of the depth and RGB image into canvas objects in the browser
Is it possible to capture the RGB image data from ZigJS and if so how?
Assuming you have plugin version 0.9.7, something along the lines of:
var plugin = document.getElementById("ZigPlugin"); // the <object> element
plugin.requestStreams(false, true, false); // tell the plugin to update the RGB image
plugin.addEventListener("NewFrame", function() { // triggered every new kinect frame
var rgbImage = Base64.decode(plugin.imageMap);
// plugin.imageMapResolution stores the resolution, right now hard-coded
// to QQVGA (160x120 for CPU-usage reasons)
// do stuff with the image
}
Also, I recommend you take the base64 decoder I wrote, from, say, http://motionos.com/webgl because it's an order of magnitude faster than the random javascript decoders I found via Google.
If you have version 0.9.8 of the plugin, there was an API change, so you should call:
plugin.requestStreams({updateImage:true});
Related
I am working on an app that uses sharp for processing photos.
Currently, when we resize and then write to buffer an image with sharp resize and toBuffer, by default the two of them wipe the EXIF data. We want to remove all metadata except for orientation (if it exists).
I've read sharp's documentation and withMetadata seems to be the candidate to achieve what I want, the problem is that withMetadata preserves all metadata and I just want the orientation of the original image.
The original line of code is
await this.sharpInstance.resize(maxDimension, maxDimension).max().toBuffer()
I think that what I want is something like
await this.sharpInstance.withMetadata().resize(maxDimension, maxDimension).max().withMetadata().toBuffer()
but only for orientation metadata.
I would really appreciated some help to solve this. Thanks very much!
Have you tried await this.sharpInstance.resize(maxDimension, maxDimension).max().withMetadata().toBuffer() as Sharp docs about withMetadata.
Edited:
I got that. So as withMetadata, first we need to save the orientation metadata and then assign to output buffer later:
// First, save the orientation for later use
const { orientation } = await this.sharpInstance.metadata();
// Then output to Buffer without metadata
// then create another Sharp instance
// from output Buffer which doesn't have metadata
// and assign saved orientation along with it
sharp(this.sharpInstance.toBuffer())
.withMetadata({ orientation }).toBuffer();
A workaround for those not specifically interested in keeping the file's original rotation plus rotation metadata: Rotate the image so that the file has no metadata, but the rotation is correct.
To do this, it is not necessary to read the metadata, if you call the rotate() method without parameters, it will look up the information in the metadata and perform the appropriate rotation.
I'm trying to stream the content of a html5 canvas on a live basis using websockets and nodejs.
The content of the html5 canvas is just a video.
What I have done so far is:
I convert the canvas to blob and then get the blob URL and send that URL to my nodejs server using websockets.
I get the blob URL like this:
canvas.toBlob(function(blob) {
url = window.URL.createObjectURL(blob);
});
The blob URLs are generated per video frame (20 frames per second to be exact) and they look something like this:
blob:null/e3e8888e-98da-41aa-a3c0-8fe3f44frt53
I then get that blob URL back from the the server via websockets so I can use it to DRAW it onto another canvas for other users to see.
I did search how to draw onto canvas from blob URL but I couldn't find anything close to what i am trying to do.
So the questions I have are:
Is this the correct way of doing what i am trying to achieve? any
pros and cons would be appreciated.
Is there any other more efficient way of doing this or I'm on a right
path?
Thanks in advance.
EDIT:
I should have mentioned that I cannot use WebRTC in this project and I have to do it all with what I have.
to make it easier for everyone where I am at right now, this how I tried to display the blob URLs that I mentioned above in my canvas using websockets:
websocket.onopen = function(event) {
websocket.onmessage = function(evt) {
var val = evt.data;
console.log("new data "+val);
var canvas2 = document.querySelector('.canvMotion2');
var ctx2 = canvas2.getContext('2d');
var img = new Image();
img.onload = function(){
ctx2.drawImage(img, 0, 0)
}
img.src = val;
};
// Listen for socket closes
websocket.onclose = function(event) {
};
websocket.onerror = function(evt) {
};
};
The issue is that when I run that code in FireFox, the canvas is always empty/blank but I see the blob URLs in my console so that makes me think that what I am doing is wrong.
and in Google chrome, i get Not allowed to load local resource: blob: error.
SECOND EDIT:
This is where I am at the moment.
First option
I tried to send the whole blob(s) via websockets and I managed that successfully. However, I couldn't read it back on the client side for some strange reason!
when I looked on my nodejs server's console, I could see something like this for each blob that I was sending to the server:
<buffer fd67676 hdsjuhsd8 sjhjs....
Second option:
So the option above failed and I thought of something else which is turning each canvas frame to base64(jpeg) and send that to the server via websockets and then display/draw those base64 image onto the canvas on the client side.
I'm sending 24 frames per second to the server.
This worked. BUT the client side canvas where these base64 images are being displayed again is very slow and and its like its drawing 1 frame per second. and this is the issue that i have at the moment.
Third option:
I also tried to use a video without a canvas. So, using WebRTC, I got the video Stream as a single Blob. but I'm not entiely sure how to use that and send it to the client side so people can see it.
IMPORTANT: this system that I am working on is not a peer to peer connection. its just a one way streaming that I am trying to achieve.
The most natural way to stream a canvas content: WebRTC
OP made it clear that they can't use it, and it may be the case for many because,
Browser support is still not that great.
It implies to have a MediaServer running (at least ICE+STUN/TURN, and maybe a gateway if you want to stream to more than one peer).
But still, if you can afford it, all you need then to get a MediaStream from your canvas element is
const canvas_stream = canvas.captureStream(minimumFrameRate);
and then you'd just have to add it to your RTCPeerConnection:
pc.addTrack(stream.getVideoTracks()[0], stream);
Example below will just display the MediaStream to a <video> element.
let x = 0;
const ctx = canvas.getContext('2d');
draw();
startStream();
function startStream() {
// grab our MediaStream
const stream = canvas.captureStream(30);
// feed the <video>
vid.srcObject = stream;
vid.play();
}
function draw() {
x = (x + 1) % (canvas.width + 50);
ctx.fillStyle = 'white';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(x - 25, 75, 25, 0, Math.PI*2);
ctx.fill();
requestAnimationFrame(draw);
}
video,canvas{border:1px solid}
<canvas id="canvas">75</canvas>
<video id="vid" controls></video>
The most efficient way to stream a live canvas drawing: stream the drawing operations.
Once again, OP said they didn't want this solution because their set-up doesn't match, but might be helpful for many readers:
Instead of sending the result of the canvas, simply send the drawing commands to your peers, which will then execute these on their side.
But this approach has its own caveats:
You will have to write your own encoder/decoder to pass the commands.
Some cases might get hard to share (e.g external media would have to be shared and preloaded the same way on all peers, and the worse case being drawing an other canvas, where you'd have to also have shared its own drawing process).
You may want to avoid intensive image processing (e.g ImageData manipulation) to be done on all peers.
So a third, definitely less performant way to do it, is like OP tried to do:
Upload frames at regular interval.
I won't go in details in here, but keep in mind that you are sending standalone image files, and hence a whole lot more data than if it had been encoded as a video.
Instead, I'll focus on why OP's code didn't work?
First it may be good to have a small reminder of what is a Blob (the thing that is provided in the callback of canvas.toBlob(callback)).
A Blob is a special JavaScript object, which represents binary data, generally stored either in browser's memory, or at least on user's disk, accessible by the browser.
This binary data is not directly available to JavaScript though. To be able to access it, we need to either read this Blob (through a FileReader or a Response object), or to create a BlobURI, which is a fake URI, allowing most APIs to point at the binary data just like if it was stored on a real server, even though the binary data is still just in the browser's allocated memory.
But this BlobURI being just a fake, temporary, and domain restricted path to the browser's memory, can not be shared to any other cross-domain document, application, and even less computer.
All this to say that what should have been sent to the WebSocket, are the Blobs directly, and not the BlobURIs.
You'd create the BlobURIs only on the consumers' side, so that they can load these images from the Blob's binary data that is now in their allocated memory.
Emitter side:
canvas.toBlob(blob=>ws.send(blob));
Consumer side:
ws.onmessage = function(evt) {
const blob = evt.data;
const url = URL.createObjectURL(blob);
img.src = url;
};
But actually, to even better answer OP's problem, a final solution, which is probably the best in this scenario,
Share the video stream that is painted on the canvas.
I have a cordova project where I have a "scribble pad" where the user can scribble their notes. This is a simple canvas object, and I'd like to get the OCR Engine to convert it into text. I'm struggling to convert the canvas data into the software bitmap that OCR Engine supports.
All the samples are based either around loading a file from the storage or reading a stream from camera. Do I have to save this canvas into a file on a device and read it back in into a stream?
I'd welcome the guidance in here as images are something I struggle with.
[Update]
So, I've managed to somehow get the stream, but unfortunately, OCR is not recognizing it.
I have the canvas object and after page is loaded, I place the text into it, so any capable OCR should be able to read it.
I also have the "img" element, for checking whether the stream is correct and contains the correct bitmap. Here is the code that handles the convas conversion to OCR recognition
var blob = canvas.msToBlob();
// This is the stream I'll use for OCR detection
var randomAccessStream = blob.msDetachStream();
// This is the stream I'll use for the image element to make sure the stream above contains what I've placed into the canvas
var blob2 = MSApp.createBlobFromRandomAccessStream("image/png", randomAccessStream.cloneStream());
// Angular JS scope model
$scope.imageUrl = URL.createObjectURL(blob2);
// This works, but returns ""
var scope = this;
if (!this.ocrEngine)
return;
var bitmapDecoder = Windows.Graphics.Imaging.BitmapDecoder;
bitmapDecoder.createAsync(randomAccessStream).then(function (decoder) {
return decoder.getSoftwareBitmapAsync();
}).then(function (bitmap) {
return scope.ocrEngine.recognizeAsync(bitmap);
}).then(function (result) {
console.log(result.text);
});
After this all runs, the image is given the src and is loaded and contains exactly whatever is in the canvas so the stream is correct.
The ocrEngine is setup the following way:
var Globalization = Windows.Globalization;
var OCR = Windows.Media.Ocr;
this.ocrEngine = OCR.OcrEngine.tryCreateFromUserProfileLanguages();
if (!this.ocrEngine) {
// Try to create OcrEngine for specified language.
// If language is not supported on device, method returns null.
this.ocrEngine = OCR.OcrEngine.tryCreateFromLanguage(new Globalization.Language("en-us"));
}
if (!this.ocrEngine) {
console.error("Selected language is not available.");
}
Why is OCR not recognizing simple 'Hello World' ?
well, that was rather embarrassing to realize that the reason why the OCR failed to read anything, even a system written text was that the resulting, generated image had a transparent background. Once I've included a rectangle with the white fill it all started to work correctly.
Unfortunately, the OCR is struggling to recognize anything I scribble on the canvas, so e.g. handwritten numbers or multiline text in canvas are not being recognized, see below
Recognized:
Not recognized
Not recognized:
Not recognized:
Then I've found Windows.UI.Input.Inking namespace and I reckon that's the only way to go.
I have seen this done by many websites, but I wonder how they do it. Some even allow one to crop the image. Is there a standard library or package for this?
You don't need any library, because It could be done in several steps. I assume you are familiar with webcam and able to show signal from it in the Video object. If you don't, in short It reads as:
var video: Video = new Video();
addChild(video);
video.smoothing = true;
video.attachCamera(camera); //Camera reference
video.width = someWidth;
video.height = someHeight;
Because Video object implements IBitmapDrawable you can draw it in the Bitmap, and do whatever you want.
var bitmapData : BitmapData = new BitmapData(_video.width, _video.height);
//Tada! You have screenshot of the current frame from video object
bitmapData.draw(cameraView);
//For testing, add as Bitmap
addChild(new Bitmap(bitmapData));
As for sending to the server, you need some server-side implementation
Here is a very usefull blog i came across (not mine)
http://matthewschrager.com/2013/05/25/how-to-take-webcam-pictures-from-browser-and-store-server-side/
I have created a webpage that receives base64 encoded bitmaps over a Websocket and then draws them to a canvas. It works perfectly. Except, the browser's (whether Firefox, Chrome, or Safari) memory usage increases with each image and never goes down. So, there must be a memory leak in my code or some other bug. If I comment out the call to context.drawImage, the memory leak does not occur (but then of course the image is never drawn). Below are snippets from my webpage. Any help is appreciated. Thanks!
// global variables
var canvas;
var context;
...
ws.onmessage = function(evt)
{
var received_msg = evt.data;
var display_image = new Image();
display_image.onload = function ()
{
context.drawImage(this, 0, 0);
}
display_image.src = 'data:image/bmp;base64,'+received_msg;
}
...
canvas=document.getElementById('ImageCanvas');
context=canvas.getContext('2d');
...
<canvas id="ImageCanvas" width="430" height="330"></canvas>
UPDATE 12/19/2011
I can work around this problem by dynamically creating/destroying the canvas every 100 images or so with createElement/appendChild and removeChild. After that, I have no more memory problems with Firefox and Chrome.
However, Safari still has a memory usage problem, but I think it is a different problem, unrelated to Canvas. There seems to be an issue with repeatedly changing the "src" of the image in Safari, as if it will never free this memory.
display_image.src = 'data:image/bmp;base64,'+received_msg;
This is the same problem described on the following site: http://waldheinz.de/2010/06/webkit-leaks-data-uris/
UPDATE 12/21/2011
I was hoping to get around this Safari problem by converting my received base64 string to a blob (with a "dataURItoBlob" function that I found on this site) and back to a URL with window.URL.createObjectURL, setting my image src to this URL, and then later freeing the memory by calling window.URL.revokeObjectURL. I got this all working, and Chrome and Firefox display the images correctly. Unfortunately, Safari does not appear to have support for BlobBuilder, so it is not a solution I can use. This is strange, since many places including the O'Reilly "Programming HTML5 Applications" book state that BlobBuilder is supported in Safari/WebKit Nightly Builds. I downloaded the latest Windows nightly build from http://nightly.webkit.org/ and ran WebKit.exe but BlobBuilder and WebKitBlobBuilder are still undefined.
UPDATE 01/03/2012
Ok, I finally fixed this by decoding the base64-encoded data URI string with atob() and then creating a pixel data array and writing it to the canvas with putImageData (see http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/). Doing it this way (as opposed to constantly modifying an image's "src" and calling drawImage in the onload function), I no longer see a memory leak in Safari or any browser.
Without actual working code we can only speculate as to why.
If you're sending the same image over and over you're making a new image every time. This is bad. You'd want to do something like this:
var images = {}; // a map of all the images
ws.onmessage = function(evt)
{
var received_msg = evt.data;
var display_image;
var src = 'data:image/bmp;base64,'+received_msg;
// We've got two distinct scenarios here for images coming over the line:
if (images[src] !== undefined) {
// Image has come over before and therefore already been created,
// so don't make a new one!
display_image = images[src];
display_image.onload = function () {
context.drawImage(this, 0, 0);
}
} else {
// Never before seen image, make a new Image()
display_image = new Image();
display_image.onload = function () {
context.drawImage(this, 0, 0);
}
display_image.src = src;
images[src] = display_image; // save it for reuse
}
}
There are more efficient ways to write that (I'm duplicating onload code for instance, and I am not checking to see if an image is already complete). I'll leave those parts up to you though, you get the idea.
you're probably drawing the image a lot more times than you are expecting to. try adding a counter and output the number to an alert or to a div in the page to see how many times the image is being drawn.
That's very interesting. This is worth reporting as a bug to the various browser vendors (my feeling is that it shouldn't happen). You might responses along the lines of "Don't do that, instead do such and such" but at least then you'll know the right answer and have an interesting thing to write up for a blog post (more people will definitely run into this issue).
One thing to try is unsetting the image src (and onload handler) right after the call to drawImage. It might not free up all the memory but it might get most of it back.
If that doesn't work, you could always create a pool of image objects and re-use them once they have drawn to the canvas. That's a hassle because you'll have to track the state of those objects and also set your pool to an appropriate size (or make it grow/shrink based on traffic).
Please report back your results. I'm very interested because I use a similar technique for one of the tightPNG encoding in noVNC (and I'm sure others will be interested too).
I don't believe this is a bug. The problem seems to be that the images are stacked on top of each other. So to clear up the memory, you need to use clearRect() to clear your canvas before drawing the new image in it.
ctx.clearRect(0, 0, canvas.width, canvas.height);
How to clear your canvas matters