How to get ImageData from image in node.js? [duplicate] - javascript

I am trying to read a image and the result i want to get is the same when you use a HTML canvas "Uint8ClampedArray"? var imageData = ctx.getImageData(0, 0, width, height); I found a NPM lib canvas but i cant get it to install.
So is there a another way to go without using Canvas?

To strictly answer the question:
So is there a another way to go without using Canvas?
The issue is that, even if we can load an image binary data, we need to be able to parse its binary format to represent it as raw pixel data (ImageData/Uint8Array objects).
This is why the canvas module needs to be compiled when installed: it rely on and links to libpng, libjpeg and other native libraries.
To load a Uint8Array representing pixel raw data from a file, without canvas (or native library wrapper), will requires decoders running only in Javascript.
For e.g. there exist decoders for png and jpeg as third-party libraries.
Decoding PNG
Using png.js:
const PNG = require('png-js');
PNG.decode('./image.png', function(pixels) {
// Pixels is a 1d array (in rgba order) of decoded pixel data
});
Decoding JPEG
Using inkjet:
const inkjet = require('inkjet');
inkjet.decode(fs.readFileSync('./image.jpg'), function(err, decoded) {
// decoded: { width: number, height: number, data: Uint8Array }
});
https://github.com/gchudnov/inkjet

Do not realy know what i did to get canvas to work. I used pixel-util to set image data.
var pixelUtil = require('pixel-util'),
Canvas = require('canvas');
var image = new Canvas.Image,
pixelUtil.createBuffer('http://127.0.0.1:8080/image/test.jpg').then(function(buffer){
image.src = buffer;
canvas = new Canvas(image.width, image.height);
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
runImage(ctx.getImageData(0, 0, canvas.width, canvas.height));
});

Related

Why is html canvas botching this source image?

I'm toying around with making a super simple HTML Canvas crop tool. The first thing I tested was to see if the output image would be perceptually identical to the input image.
Using this image as a source, canvas fails to maintain the smooth gradients as you can see in the image comparison I posted here (still visible despite the imgur compression). You can also replicate it in any online photo editor such as https://pixlr.com.
Is there some way to fix this?
Code snippet I am using:
const loadImageToCanvas = (file) => { // file is from input.files
const img = new Image();
img.onload = () => {
const { width, height } = img;
canvas.width = width;
canvas.height = height;
ctx.clearRect(0, 0, width, height);
ctx.drawImage(img, 0, 0);
}
img.src = URL.createObjectURL(file);
};
Two words: gamma correction. Your PNG file has a gAMA chunk of 1.0000. Web browsers are (correctly) using this information to adjust the displayed pixels for an output device having the standard sRGB gamma of 2.2. This behaviour is the same for both <canvas> and <img> elements.[1]
I don't know what viewer or conversion tool you are using to produce your imgur image, but it is either stripping or ignoring the gamma chunk.
If your image is in fact encoded with a gamma of 2.2 (and thus the gamma chunk is erroneous), you can remove the chunk with:
pngcrush -rem gAMA 1024.png 1024.nogamma.png
[1] The spec mandates this consistency. Are you really seeing different behaviour between your (correct, although using createObjectURL is unnecessary and a bad idea) code and an <img> tag?

How to select file, fix orientation, and add to canvas

My site allows users to upload their own photos. I've learned that this is trickier than first expected and although I can get each of the components working individually I'm not successfully getting them to produce the outcome I need when combining them. I believe the issue is my lack of understanding around the outputs of the functions i'm using.
I think what I need to do is:
Let the user choose the file to upload
Check / fix the orientation
Display the image in a canvas
Convert the canvas to base64 (to upload to Firebase)
Upload the item
Let the user choose the file to upload
$(document).on('change','#fileUpload',function(e) {
correctImageOrientation(e);
});
Check / fix the image orientation
I'm using image-load (link) for my orientation changes.
function correctImageOrientation(e) {
loadImage(
e.target.files[0],
function (img) {
// document.body.appendChild(img); // Note: this successfully appends the image in the correct orientation to the body - but I need to do this to a canvas to I call:
addImageToCanvas(img);
},
{
orientation: true
}
);
Display the image in a canvas
This part fails. If I pass in the selected file using e.target.files[0] and create an objectURL then it works. This is why I think the img i'm passing in needs to be read / displayed on the canvas differently.
function addImageToCanvas(img) {
img.onload = function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, 300, 300);
console.log("Ending img.onload"); // This is never reached
}
}
I can then successfully convert the file to base64 and upload to Firebase.
I think there must be an easier way to achieve what I want which is for people to upload an image, for the orientation to be correct, and for this to be stored online.
EDIT: To store the image in firebase I am using this to convert the image back to a blob.
I have replaced addImageToCanvas with the function below which was taken then modified from David Walsh's site. Because I'm passing in an image I needed to convert this to a canvas.
function convertImageToCanvas(image) {
var canvas = document.getElementById("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
}

LocalStorage phonegap camera image

i'm trying to use localStorage to save an image, or multiple images for retrieval at a later date to upload to a server.
The current camera code is as follows:
function capturePhoto() {
navigator.camera.getPicture(onCameraSuccess, onCameraFail, {quality: 70, destinationType : Camera.DestinationType.DATA_URL});
}
function onCameraSuccess(imageData) {
//In our success call we want to first process the image to save in our image box on the screen.
var image = document.getElementById('image');
image.src = "data:image/jpeg;base64," + imageData;
//Create a new canvas for our image holder
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture
imgCanvas.width = image.width;
imgCanvas.height = image.height;
// Draw image into canvas element
imgContext.drawImage(image, 0, 0, image.width, image.height);
// Get canvas contents as a data URL
var imgAsDataURL = imgCanvas.toDataURL("image/png");
// Save image into localStorage
try {
// localStorage.setItem(“savedImage”, imgAsDataURL);
localStorage.setItem("savedImage", imageData);
alert('Image Saved');
}
catch (e) {
alert("Storage failed: " + e);
}
var imageStorage = localStorage.getItem("savedImage");
// myCardHolder= document.getElementById(“m1-cardStorage-image1″);
// Reuse existing Data URL from localStorage
var imageInfo = document.getElementById('image');
imageInfo.src = "data:image/jpeg;base64," + imageStorage;
}
This triggers the camera, and the image captured is displayed into
<img id="image" src=""></img>
It also draws a canvas to output the image into. What i'm really trying to achieve is to capture the images base64 data to be able to store it into an array so that it may be uploaded/downloaded from a server.
Ideally i'd like to completely avoid having to display the image to the user, and simply store the images data
I may have misunderstood the localStorage/camera api a little, so any pointers would be great.
Does the image HAVE to be output into an element before the data can be stored? If i could just output it into the canvas that may never have to be shown, and extract the data from the canvas element?
Does the image HAVE to be output into an element before the data can be stored?
Not at all, in this case anyways. You are already receiving the image as base64 data so just store that directly.
Problems:
datauris can be chopped by the browser if too long
if not chopped by browser on string level, the data can be chopped by localstorage itself which has a size limit (i think it's currently around 5 mb for most browsers but there is no standard here)
a string uses two bytes per char so the storage is in effect the half
A better local storage is to use indexedDB.
When you read the base64 data, then you have to use an Image to show the data. Just prefix as you do with data:... etc. and remember to use correct file type.
Last year I was trying to solve the same problem, I don't have the code right now but I followed kind of the approach taken on this answer:
How to convert image into base64 string using javascript
Remember that localStorage has a limit of 5 MB, so if you save a lot of images in b64 you can reach that limit easily. (which was my case), so I had to move my storage to somewhere else, like a sqlite or something like that.

Why does canvas.toDataURL() not produce the same base64 as in Ruby for an image?

I'm trying to produce the same base64 data for an image file in both JavaScript and in Ruby. Unfortunately both are outputting two very different values.
In Ruby I do this:
Base64.encode64(File.binread('test.png'));
And then in JavaScript:
var image = new Image();
image.src = 'http://localhost:8000/test.png';
$(image).load(function() {
var canvas, context, base64ImageData;
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
context.drawImage(this, 0, 0);
imageData = canvas.toDataURL('image/png').replace(/data:image\/[a-z]+;base64,/, '');
console.log(imageData);
});
Any idea why these outputs are different?
When you load the image in Ruby the binary file without any modifications will be encoded directly to base-64.
When you load an image in the browser it will apply some processing to the image before you will be able to use it with canvas:
ICC profile will be applied (if the image file contains that)
Gamma correction (where supported)
By the time you draw the image to canvas, the bitmap values has already been changed and won't necessarily be identical to the bitmap that was encoded before loading it as image (if you have an alpha channel in the file this may affect the color values when drawn to canvas - canvas is a little peculiar at this..).
As the color values are changed the resulting string from canvas will naturally also be different, before you even get to the stage of re-encoding the bitmap (as PNG is loss-less the encoding/compressing should be fairly identical, but factors may exist depending on the browser implementation that will influence that as well. to test, save out a black unprocessed canvas as PNG and compare with a similar image from your application - all values should be 0 incl. alpha and at the same size of course).
The only way to avoid this is to deal with the binary data directly. This is of course a bit overkill (in general at least) and a relative slow process in a browser.
A possible solution that works in some cases, is to remove any ICC profile from the image file. To save an image from Photoshop without ICC choose "Save for web.." in the file menu.
The browser is re-encoding the image as you save the canvas.
It does not generate an identical encoding to the file you rendered.
So I actually ended up solving this...
Fortunately I am using imgcache.js to cache images in the local filesystem using the FileSystem API. My solution is to use this API (and imgcache.js makes it easy) to get the base64 data from the actual cached copy of the file. The code looks like this:
var imageUrl = 'http://localhost:8000/test.png';
ImgCache.init(function() {
ImgCache.cacheFile(imageUrl, function() {
ImgCache.getCachedFile(imageUrl, function(url, fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
console.log($.md5(this.result.replace(/data:image\/[a-z]+;base64,/, '')));
};
reader.readAsDataURL(file);
});
});
});
});
Also, and very importantly, I had to remove line breaks from the base64 in Ruby:
Base64.encode64(File.binread('test.png')).gsub("\n", '');

canvas.toDataURL("image/png") - how does it work and how to optimize it

I wanted to know if there was anyone out there that knows how
canvas.toDataURL("image/png");
works? I want to understand better because at times it seems to really slow my computer down.
Is there a way to optimize the base64 image before during or after to get better performance ?
function base64(url) {
var dataURL;
var img = new Image(),
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = url;
img.crossOrigin = "Anonymous";
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL('image/png');
preload(dataURL);
canvas = null;
};
img.src = url;
}
Basically this is my function but I wanted to see if there was a way to make this process perform better or if there was an alternative to canvas.toDataURL('image/png');
thanks
toDataURL() does the following when called (synchronously):
Creates a file header based on the file type requested or supported (defaults to PNG)
Compresses the bitmap data based on file format
Encodes the resulting binary file to Base-64 string
Prepends the data-uri header and returns the result
When setting a data-uri as source (asynchronously):
String is verified
Base-64 part is separated and decoded to binary format
Binary file verified then parsed and uncompressed
Resulting bitmap set to Image element and proper callbacks invoked
These are time-consuming steps and as they are internal we cannot tap into them for any reason. As they are pretty optimized as they are given the context they work in there is little we can do to optimize them.
You can experiment with different compression schemes by using JPEG versus PNG. They are using very different compression techniques and depending on the image size and content one can be better than the other in various situations.
My 2 cents..
The high performance alternative is canvas.toBlob. It is extremely fast, asynchronous, and produces a blob which can also be swapped to disk, and is subjectly speaking simply far more useful.
Unfortunately it is implemented in Firefox, but not in chrome.
Having carefully bench-marked this, there is no way around because canvas.toDataURL itself is the bottleneck by orders of magnitude.

Categories

Resources