Saving current work from multiple canvas elements - javascript

Here is my problem: I have created an image collage function in javascript. (I started off with some code from this post btw: dragging and resizing an image on html5 canvas)
I have 10 canvas elements stacked on top of each other and all parameters, including 2dcontext, image data, positions etc. for each canvas is held in instances of the function 'collage'.
This is working fine, I can manipulate each canvas separately (drag, resize, adding frames, etc). But now and I want the user to be able to save the current work.
So I figure that maybe it would be possible to create a blob, that contains all the object instances, and then save the blob as a file on disk.
This is the function collage (I also push each instance to the array collage.instances, to be able to have numbered indexes)
function collage() {
this.canvas_board = '';
this.canvas = '';
this.ctx = '';
this.canvasOffset = '';
this.offsetX = '';
this.offsetY = '';
this.startX = '';
this.startY = '';
this.imageX = '';
this.imageY = '';
this.mouseX = '';
this.mouseY = '';
this.imageWidth = '';
this.imageHeight = '';
this.imageRight = '';
this.imageBottom = '';
this.imgframe = '';
this.frame = 'noframe';
this.img = '';
collage.instances.push(this);
}
collage.instances = [];
I tried with something like this:
var oMyBlob = new Blob(collage.instances, {type: 'multipart/form-data'});
But that doesn't work (only contains about 300 bits of data).
Anyone who can help? Or maybe suggest an alternative way to save the current collage work. It must of course must be possible to open the blob and repopulate the object instances.
Or maybe I am making this a bit more complicated than it has to be... but I am stuck right now, so I would appreciate any hints.

You can extract each layer's image data to DataURLs and save the result as a json object.
Here's a quick demo: http://codepen.io/gunderson/pen/PqWZwW
The process literally takes each canvas and saves out its data for later import.
The use of jquery here is for convenience:
$(".save-button").click(function() {
var imgData = JSON.stringify({
layers: getLayerData()
});
save(imgData, "myfile.json");
});
function save(filecontents, filename) {
try {
var $a = $("<a>").attr({
href: "data:application/json;," + filecontents,
download: filename
})[0].click();
return filecontents;
} catch (err) {
console.error(err);
return null;
}
}
function getLayerData() {
var imgData = [];
$(".layer").each(function(i, el) {
imgData.push(el.toDataURL("image/png"));
});
return imgData;
}
To restore, you can use a FileReader to read the contents of the JSON back into the browser, then make <img>s for each layer, set img.src to the dataURLs in your JSON and from there you can draw the <img> into onload canvases.

Add a reference (src URL) for the image to the instance, then serialize the instance array as JSON and use f.ex. localStorage.
localStorage.setItem("currentwork", JSON.stringify(collage.instances));
Then to restore you would need to do:
var tmp = localStorage.getItem("currentwork");
collage.instances = tmp ? JSON.parse(tmp) : [];
You then need to iterate through the array and reload the images using proper onload handling. Finally re-render everything.
Can you store image data on client? Yes, but not recommended. This will take a lot of space and if too much you will not be able to save all the data, the user may refuse to allow more storage space etc.
Keeping a link to the image on a server is a better approach for these things IMO. But if you disagree, look into IndexedDB (or WebSQL although deprecated) to have local storage which can be expanded in available space. localStorage can only hold between 2.5 - 5 mb, ie. no image data and only strings. Each char takes two bytes, data-uris adds 33% on top, so this will run empty pretty fast...

Related

how to read a static file from url in javascript to create an array

I searched but I don't find, I am coding a simulator and I want to do the calculus using javascript, the simulator takes 2 kind of entries. The first entries are given by user, this part is done. The second part is a lot of coefficient which are stored in csv/tsv file, the file is uploaded on the server. And I am not able to read this file, I found a lot of code on how to convert csv to array and I think that I will be able to do it alone. For now I am doing step by step so I just want to read the csv file to put it inside a table, when I use the code shown it works if I use an < input type="file" > but I am not able to make it works with a static url. Can You help me?
function myprocessFile()
{
var fileSize = 0;
var theFile = document.getElementById("myFile").files[0];
document.getElementById("toto").innerHTML = blob;
if (theFile)
{
var table = document.getElementById("myTable");
var headerLine = "";
var myReader = new FileReader();
myReader.onload = function(e)
{
// CREATE TABLE
}
myReader.readAsText(theFile);
}
return false;
}
You could use the fetch API :
fetch('url/to/your/csv/file')
.then(function(response) {
return response.text()
})
.then(function(csv) {
// convert your csv to an array
});

tensorflow js RGB to Lab from input img

I have been searching and debugging but I can not find anything that works for me. I'm doing a web application in which I try to go from black and white images to color and for that I have put an input in which I load the image and make the inference (currently with an image-to-image model).
The fact is that I want to transform the image of rgb to lab as a preprocess before it enters the network because that is how I intend to train it. My code is as follows:
var myInput = document.getElementById('myFileInput');
function processPic() {
if (myInput.files && myInput.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#prev_img_id').attr('src', e.target.result);
//Initiate the JavaScript Image object.
var image = new Image();
//Set the Base64 string return from FileReader as source.
image.src = e.target.result;
image.onload = function () {
//alert(this.height)
const webcamImage = tf.fromPixels(this);
const batchedImage = webcamImage.expandDims(0);
predict(batchedImage.toFloat().div(tf.scalar(127)).sub(tf.scalar(1)))
}
}
reader.readAsDataURL(myInput.files[0]);
}
}
myInput.addEventListener('change', processPic, false);
function predict(the_img) {
//get predictions
let pred = mobilenet.predict(the_img);
//retreive the highest probability class label
let cls = pred.argMax().buffer().values[0];
alert(IMAGENET_CLASSES[cls]);
}
I wrote a some code to do this using the tensorflow.js operations the code can be optimized further with matrix multiplications but it will work and should put you on the right path if this is still relevant.
RGB2LAB TFJS
There are some resources around regarding the conversion RGB to LAB e.g. http://www.easyrgb.com/en/math.php.
You could also give this JS implementation a try (https://github.com/antimatter15/rgb-lab, which is actually using the equations from the easyrgb website), calling the rgb2lab() function inside your image.onload.
To access the image data required by ``, you can have a look at this SO thread (How do I access/change pixels in a javascript image object?) i.e. using an intermediary canvas.

Specify the type returned from FileReader readAsDataURL() as PNG and not TIFF?

I'm getting an image that has been pasted into the content editable div and during paste the types in listed in the Clipboard include "image/png" and "image/tiff".
When I get that image using a FileReader and readAsDataURL the base64 image string it returns is a TIFF, "data:image/tiff,".
Is there a way to specify to the FileReader to return a PNG, "data:image/png"?
Related question here.
You don't.
FileReader.readAsDataURL() only converts the binary data held in the passed Blob to a base64 string, it doesn't do any other conversion.
It will only prepend the corresponding MIME header to whatever it thinks this data is (this check is first done against the Blob's type, and then probably against magic-numbers), but you can't force it to convert the data to something else.
const fr = new FileReader();
const fakeGif = new Blob(['foo'], {type:'image/gif'});
fr.onload = onload;
fr.readAsDataURL(fakeGif);
function onload(){
console.log(fr.result); // with image/gif MIME
console.log(atob(fr.result.split(',')[1])) // still just "foo"...
}
Now to your case, since Safari still doesn't support the DataTransferItem interface, you are out of luck.
Even though Grab utility does save a png File in the clipboard, Safari chose to display the tiff raw data.
You can check this assertion in your dev's Network panel, where you will see that the image is fetched as image/tiff.
Browsers that do support DataTransferItem interface can retrieve the png file attached in the clipboard though, so let's hope Safari implement this interface soon enough.
So one way to convert this TIFF image to a png one, is to go through a canvas element:
// an Array to hold pasted Files
var files = [];
// We start by checking if the browser supports the
// DataTransferItem interface. If not, we need to create a
// contenteditable element that catches all pasted data
if (!window.DataTransferItem) {
var pasteCatcher = document.createElement("div");
pasteCatcher.setAttribute("contenteditable", "");
// We can hide the element and append it to the body,
pasteCatcher.style.opacity = 0.5;
document.body.appendChild(pasteCatcher);
// as long as we make sure it is always in focus
pasteCatcher.focus();
document.addEventListener("click", function() {
pasteCatcher.focus();
});
}
// Add the paste event listener
window.addEventListener("paste", pasteHandler);
/* Handle paste events */
function pasteHandler(e) {
// We need to check if event.clipboardData is supported (Chrome)
if (e.clipboardData) {
// Get the items from the clipboard
var items = e.clipboardData.items || e.clipboardData.files;
itemcount = items.length;
if (itemcount) {
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
getItem(items[i]);
}
} else {
// This is a cheap trick to make sure we read the data
// AFTER it has been inserted.
setTimeout(checkInput, 1);
}
// If we can't handle clipboard data directly (Firefox),
// we need to read what was pasted from the contenteditable element
} else {
console.log("checking input");
// This is a cheap trick to make sure we read the data
// AFTER it has been inserted.
setTimeout(checkInput, 1);
}
}
/* For browsers that support DataTransferItem interface */
function getItem(item)  {
if (item.type.indexOf("image") !== -1) {
// We need to represent the image as a file,
var blob = item instanceof Blob ? item : item.getAsFile();
// save the File for later use if needed
files.push(blob);
// and use a URL or webkitURL (whichever is available to the browser)
// to create a temporary URL to the object
var URLObj = window.URL || window.webkitURL;
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
createImage(source);
}
}
/* For older browsers */
/* Parse the input in the paste catcher element */
function checkInput() {
// Store the pasted content in a variable
var child = pasteCatcher.childNodes[0];
if (child) {
// If the user pastes an image, the src attribute
// will represent the image as a base64 encoded string.
if (child.tagName === "IMG") {
getPngFromIMG(child, function(blob) {
// Clear the inner html to make sure we're always
// getting the latest inserted content
pasteCatcher.innerHTML = "";
// save the png blob in our list
files.push(blob);
createImage(URL.createObjectURL(blob));
});
}
}
}
function getPngFromIMG(img, callback) {
if (img.naturalWidth) // if already loaded
draw();
else
img.onload = draw;
function draw() {
var canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0);
canvas.toBlob(callback);
}
}
/* Creates a new image from a given source */
function createImage(source) {
var pastedImage = new Image();
pastedImage.onload = function(e) {
loadImage.src = e.target.src;
}
pastedImage.src = source;
}
btn.onclick = function() {
console.log(files);
}
<textarea id="pasteArea" placeholder="Paste Image Here"></textarea>
<img id="loadImage" />
<button id="btn">do something with pasted files</button>

Split A large JSON into Smaller parts in Javascript/jQuery

I have a file upload functionality in my application which can not upload JSON files which are more than 10MB in size. If user uploads a file >= 10 MB , My app should split it into smaller JSON files each less than 10MB. Also, the Proper JSON objects needs to be maintained in the new low-sized files.
Is there a way to do this in Javascript or jQuery?
I propose a solution like this without any specific library. It does use a bit of modern techniques but maybe useful to you:
var openFile = function(event, callback) {
// get target input
var input = event.target;
// create an instance of filereader
var reader = new FileReader();
// define handler to get results
reader.onload = function(e){
var contents = e.target.result;
// use a promise maybe to make this neater
callback(contents);
};
// make sure you tell it to read as text
// also maybe add some validation on your input
// for correct types
reader.readAsText(input.files[0]);
};
var getChunks = function(str){
var chunks = [];
// not best at these things but this should be
// around 1mb max
var chunkSize = 1000000;
// while the chunk is less than the size indicated it goes
// into the same item of array
while (str) {
if (str.length < chunkSize) {
chunks.push(str);
break;
}
else {
chunks.push(str.substr(0, chunkSize));
str = str.substr(chunkSize);
}
}
return chunks;
}
var fileInput = document.querySelector('#jsonUpload');
fileInput.addEventListener('change', function(event){
openFile(event, function(str){
console.log(getChunks(str));
});
});
Then it would read the json file from:
<input type='file' accept='*' id="jsonUpload">
Link to the fiddle

Load file into IMAGE object using Phantom.js

I'm trying to load image and put its data into HTML Image element but without success.
var fs = require("fs");
var content = fs.read('logo.png');
After reading content of the file I have to convert it somehow to Image or just print it to canvas. I was trying to conver binary data to Base64 Data URL with the code I've found on Stack.
function base64encode(binary) {
return btoa(unescape(encodeURIComponent(binary)));
}
var base64Data = 'data:image/png;base64,' +base64encode(content);
console.log(base64Data);
Returned Base64 is not valid Data URL. I was trying few more approaches but without success. Do you know the best (shortest) way to achieve that?
This is a rather ridiculous workaround, but it works. Keep in mind that PhantomJS' (1.x ?) canvas is a bit broken. So the canvas.toDataURL function returns largely inflated encodings. The smallest that I found was ironically image/bmp.
function decodeImage(imagePath, type, callback) {
var page = require('webpage').create();
var htmlFile = imagePath+"_temp.html";
fs.write(htmlFile, '<html><body><img src="'+imagePath+'"></body></html>');
var possibleCallback = type;
type = callback ? type : "image/bmp";
callback = callback || possibleCallback;
page.open(htmlFile, function(){
page.evaluate(function(imagePath, type){
var img = document.querySelector("img");
// the following is copied from http://stackoverflow.com/a/934925
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
window.dataURL = canvas.toDataURL(type);
}, imagePath, type);
fs.remove(htmlFile);
var dataUrl = page.evaluate(function(){
return window.dataURL;
});
page.close();
callback(dataUrl, type);
});
}
You can call it like this:
decodeImage('logo.png', 'image/png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
or this
decodeImage('logo.png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
I tried several things. I couldn't figure out the encoding of the file as returned by fs.read. I also tried to dynamically load the file into the about:blank DOM through file://-URLs, but that didn't work. I therefore opted to write a local html file to the disk and open it immediately.

Categories

Resources