Concat Buffers or alternative method - javascript

I want to resize image screenshot of webshot module without touching disk, doing all in memory.
Here is what I have
var webshot = require('webshot');
var fs = require('fs');
var sharp = require('sharp');
alldata = new Buffer(1024*1024);
var options= {
windowSize: {
width: 1024
, height: 768
},
zoomFactor:0.25,
renderDelay:500,
quality:50,
phantomConfig: {'ignore-ssl-errors': 'true'}
};
var file = fs.createWriteStream('google.png', {encoding: 'binary'});
var renderStream = webshot('google.com', options);
var completed = false;
renderStream.on('data', function(data) {
console.log(Type(data));
alldata.write(data);
});
renderStream.on('end', function(data) {
completed=true;
});
require('deasync').loopWhile(function(){return !completed;});
Since the data will be delivered in chunks, I need to combine Buffers and at the end convert the image to another size using Sharp:
var resizeTransform = sharp(thebuffer).resize(320, 270).max();
But I just can't concat buffers and not sure how to do it directly with Sharp without concatenating buffers. Any ideas how to do it properly?

You can use pipe to resize your image.
var webshot = require('webshot');
var fs = require('fs');
var sharp = require('sharp');
var options = {
windowSize: {
width: 1024,
height: 768
},
zoomFactor: 0.25,
renderDelay: 500,
quality: 50,
phantomConfig: { 'ignore-ssl-errors': 'true' }
};
var file = fs.createWriteStream('google.png', { encoding: 'binary' });
var renderStream = webshot('google.com', options);
const resizeStream = sharp().resize(320, 270).png();
//pipe your stream, get the webshot, resize it, then save to png
renderStream.pipe(resizeStream).pipe(file);
//since res is a writable stream, you can pipe the stream down to it just like the file stream.
//renderStream.pipe(resizeStream).pipe(res);

Related

Use Image URL to train Tensorflowjs program

I'm trying to use images off the internet to try and train my network. I'm using an Image() object to create the images and pass them to tensorflow. According to my knowledge, Image() returns a HTMLImageElement, however, I'm still getting the following error:
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 Image
Below is the code I'm running:
const tf = require("#tensorflow/tfjs");
require("#tensorflow/tfjs-node")
const mobilenetModule = require("#tensorflow-models/mobilenet");
const knnClassifier = require("#tensorflow-models/knn-classifier");
const { Image } = require("canvas");
const classifier = knnClassifier.create();
const urls = ["https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Solid_white.svg/2048px-Solid_white.svg.png", "https://stone.co.nz/wp-content/uploads/2020/06/Iconic-Black.jpg", "https://media.tarkett-image.com/large/TH_25094225_25187225_001.jpg"];
async function start() {
const mobilenet = await mobilenetModule.load();
const pic0 = new Image();
pic0.src = urls[0];
pic0.onload = () => {
const img0 = tf.browser.fromPixels(pic0);
const logits0 = mobilenet.infer(img0, true);
classifier.addExample(logits0, 0);
}
const pic1 = new Image();
pic1.src = urls[1];
pic1.onload = () => {
const img1 = tf.browser.fromPixels(pic1);
const logits1 = mobilenet.infer(img1, true);
classifier.addExample(logits1, 1);
}
const checkPic = new Image();
checkPic.src = urls[2];
checkPic.onload = () => {
const x = tf.browser.fromPixels(checkPic);
const xlogits = mobilenet.infer(x, true);
const p = classifier.predictClass(xlogits);
console.log(p);
}
}
start();
Please note that I am a js/nodejs newbie 😊
Seems like an oversight by TFJS team so Image type is not recognized.
do this instead:
const pic0 = document.createElement('image')
This creates HTMLImageElement which is "almost" the same as Image element, but has a different signature that TF expects
And best to report on TFJS Git as an issue, it should be easy to resolve

Sending image manipulated via JS in AJAX POST request

I'm a server-side dev learning the ropes of vanilla JS. I need to clear my concepts regarding sending an Ajax POST request for an image object I'm creating in JS - this question is about that.
Imagine a web app where users upload photos for others to see. At the point of each image's upload, I use vanilla JS to confirm the image's mime-type (via interpreting magic numbers), and then resize the image for optimization purposes.
After resizing, I do:
var canvas = document.createElement('canvas');
canvas.width = resized_width;
canvas.height = resized_height;
var ctx = canvas.getContext("2d");
ctx.drawImage(source_img, 0, 0, resized_width, resized_height);
var resized_img = new Image();
resized_img.src = canvas.toDataURL("image/jpeg",0.7);
return resized_img;
The image object returned has to be sent to the backend via an Ajax request. Something like:
function overwrite_default_submit(e) {
e.preventDefault();
var form = new FormData();
form.append("myfile", resized_img, img_name);
var xhr = new XMLHttpRequest();
xhr.open('POST', e.target.action);
// xhr.send(form); // uncomment to really send the request
}
However, the image object returned after resizing is essentially an HTML element like so <img src="data:image/jpeg;base64>. Whereas the object expected in the FormData object ought to be a File object, e.g. something like: File { name: "example.jpg", lastModified: 1500117303000, lastModifiedDate: Date 2017-07-15T11:15:03.000Z, webkitRelativePath: "", size: 115711, type: "image/jpeg" }.
So what do I do to fix this issue? Would prefer to learn the most efficient way of doing things here.
Btw, I've seen an example on SO of using the JS FILE object, but I'd prefer a more cross-browser method, given File garnered support from Safari, Opera Mobile and built-in Android browsers relatively recently.
Moreover, only want pure JS solutions since I'm using this as an exercise to learn the ropes. JQuery is on my radar, but for later.
The rest of my code is as follows (only included JPEG processing for brevity):
var max_img_width = 400;
var wranges = [max_img_width, Math.round(0.8*max_img_width), Math.round(0.6*max_img_width),Math.round(0.4*max_img_width),Math.round(0.2*max_img_width)];
// grab the file from the input and process it
function process_user_file(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = process_image;
reader.readAsArrayBuffer(file.slice(0,25));
}
// checking file type programmatically (via magic numbers), getting dimensions and returning a compressed image
function process_image(e) {
var img_width;
var img_height;
var view = new Uint8Array(e.target.result);
var arr = view.subarray(0, 4);
var header = "";
for(var i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
switch (header) {
case "ffd8ffe0":
case "ffd8ffe1":
case "ffd8ffe2":
case "ffd8ffe3":
case "ffd8ffe8":
// magic numbers represent type = "image/jpeg";
// use the 'slow' method to get the dimensions of the media
img_file = browse_image_btn.files[0];
var fr = new FileReader();
fr.onload = function(){
var dataURL = fr.result;
var img = new Image();
img.onload = function() {
img_width = this.width;
img_height = this.height;
resized_img = resize_and_compress(this, img_width, img_height, 80);
}
img.src = dataURL;
};
fr.readAsDataURL(img_file);
to_send = browse_image_btn.files[0];
load_rest = true;
subform.disabled = false;
break;
default:
// type = "unknown"; // Or one can use the blob.type as fallback
load_rest = false;
subform.disabled = true;
browse_image_btn.value = "";
to_send = null;
break;
}
}
// resizing (& compressing) image
function resize_and_compress(source_img, img_width, img_height, quality){
var new_width;
switch (true) {
case img_width < wranges[4]:
new_width = wranges[4];
break;
case img_width < wranges[3]:
new_width = wranges[4];
break;
case img_width < wranges[2]:
new_width = wranges[3];
break;
case img_width < wranges[1]:
new_width = wranges[2];
break;
case img_width < wranges[0]:
new_width = wranges[1];
break;
default:
new_width = wranges[0];
break;
}
var wpercent = (new_width/img_width);
var new_height = Math.round(img_height*wpercent);
var canvas = document.createElement('canvas');
canvas.width = new_width;
canvas.height = new_height;
var ctx = canvas.getContext("2d");
ctx.drawImage(source_img, 0, 0, new_width, new_height);
console.log(ctx);
var resized_img = new Image();
resized_img.src = canvas.toDataURL("image/jpeg",quality/100);
return resized_img;
}
Update: I'm employing the following:
// converting image data uri to a blob object
function dataURItoBlob(dataURI,mime_type) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
return new Blob([ab], { type: mime_type });
}
Where the dataURI parameter is canvas.toDataURL(mime_type,quality/100)
You should call the canvas.toBlob() to get the binary instead of using a base64 string.
it's async so you would have to add a callback to it.
canvas.toBlob(function(blob) {
resized_img.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(this.url);
};
// Preview the image using createObjectURL
resized_img.src = URL.createObjectURL(blob);
// Attach the blob to the FormData
var form = new FormData();
form.append("myfile", blob, img_name);
}, "image/jpeg", 0.7);
See to this SO post: How to get base64 encoded data from html image
I think you need to call 'canvas.toDataURL()' to get the actual base64 stream of the image.
var image = canvas.toDataURL();
Then upload it with a Form: Upload a base64 encoded image using FormData?
var data = new FormData();
data.append("image_data", image);
Untested, but this should be about it.

Loading image to canvas after retrieving from camera roll (cordova)

I'm fetching a file from the iOS camera roll, which returns a uri in this form:
file:///var/mobile/Containers/Data/Application/90849385-6FC7-4C4E-8220-7585312C3DFB/tmp/filename.jpg
I receive this uri from this function:
function openFilePicker(selection) {
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var options = setOptions(srcType);
var func = createNewFileEntry;
navigator.camera.getPicture(function cameraSuccess(imageUri) {
loadToCanvas(imageUri);
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
and pass it to this function:
function loadToCanvas(path) {
console.log(path);
var ctx = document.getElementById('image');
ctx = ctx.getContext('2d');
var hero = new Image();
hero.onload = function () {
ctx.drawImage(hero, 0, 0);
};
hero.src = path;
}
The image is not showing in the canvas but there are no errors in the evothings console. What am I missing? Surely if cordova has the means of returning this uri to me then I should have access to the file, yeah? Thanks!

Esri Take Map Scrrenshots using Javascript

I am trying to generate a pdf report including current map screenshot of ESRI map using JavaScript. Here is my code.
var getCurrentMapScreenShot = function (success, error) {
esriLoader.Config.defaults.io.proxyUrl = myAppSettingsModel.SettingsModel.MapSettings.AGSProxyURL;
esriLoader.Config.defaults.io.alwaysUseProxy = true;
var printTask = new esriLoader.PrintTask("myexportUrl");
var template = new esriLoader.PrintTemplate();
template.exportOptions = {
width: 600,
height: 600,
dpi: 96
};
template.format = "image/png";
template.layout = "MAP_ONLY",
template.preserveScale = true;
template.layoutOptions = {
legendLayers: [], // empty array means no legend
scalebarUnit: "Miles"
};
var params = new esriLoader.PrintParameters();
params.map = map;
params.template = template;
printTask.execute(params, success, error);
}
This function will give you an event that contains a url , Then I am passing this url to get map base64 data.
Here is the function calling.
map.GetCurrentMapScreenShot(function (event) {
var mapScreenShotURL = event.url;
Factory.GetBase64ForImgUrl(mapScreenShotURL,
function (mapImageBase64Encoded) {});
and here is the function that converts url to base64image
function getBase64ForImgUrl(url, callback, outputFormat) {
console.log("#################### Summary Report Image " + url);
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
// Clean up
canvas = null;
};
img.src = url;
}
I am getting base64 image data of the map , But the problem is that, I am getting blurred image of the map with no feature layers and no icons..And also map contains some junk texts.
Thanks
I resolved the issue almost, Now I am getting the map image that contains map Icons and layer graphics, But unfortunately still getting some junk texts.
I have changed the code as follows.
template.format = "JPG";
template.layout = "A4 Landscape",
template.preserveScale = false;
template.layoutOptions = {
"legendLayers": [], // empty array means no legend
"scalebarUnit": "Miles"
}

using angularjs and JIC to upload file to S3

I have angular upload to S3 working, but I would like to compress (or shrink) the image file down to height 250 px. Using angular upload:
$scope.onFileSelect = function($files) {
$scope.files = $files;
$scope.upload = [];
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
console.log('file: ' + JSON.stringify(file));
file.progress = parseInt(0);
....
and then process with the upload. This works great... except for the size issue. So, I ended up trying to use a library called JIC (https://github.com/brunobar79/J-I-C) to handle the compression. The result of my console.log(file) is:
file: {"webkitRelativePath":"","lastModifiedDate":"2014-04-25T19:35:22.000Z","name":"pooltable.jpg","type":"image/jpeg","size":1922990}
I try passing this file to compress and then proceed to upload to S3, but it does not work:
var file = compress($files[i], 50, $files[i].type);
Here is the compress code from JIC which I modified to fit my heigh requirement:
var compress = function(source_img_obj, quality, output_format){
var mime_type = "image/jpeg";
if(output_format != undefined && output_format == "png"){
mime_type = "image/png";
}
var cvs = document.createElement('canvas');
var ratio = 250 / source_img_obj.naturalHeight;
cvs.width = ratio * source_img_obj.naturalWidth;
cvs.height = 250;
var ctx = cvs.getContext("2d").drawImage(source_img_obj, 0, 0);
var newImageData = cvs.toDataURL(mime_type, ratio/100);
var result_image_obj = new Image();
result_image_obj.src = newImageData;
return result_image_obj;
};
The problem here is that my source_img_obj doesn't come from HTML, it comes from the user uploading the image from their local machine so it does not have a src property and I get this error:
Failed to execute 'drawImage' on 'CanvasRenderingContext2D': No function was found that matched the signature provided.
I don't actually want to draw the images into my html file, I just want to shrink them and upload them directly to the server.

Categories

Resources