I just wrote a little script that resizes uploaded images into canvas thumbnails. While resizing images it slows down the rest of the web page.
I just discovered web workers which I was hoping I could use to alleviate this problem by moving the resizing task into the background.
The MDN article says you can't access the DOM, which is fine, but I was hoping I could still create DOM elements and pass a canvas back.
However, the Image object doesn't even seem to exist from inside my worker thread. Chrome tells me:
Uncaught ReferenceError: Image is not defined resize_image.js:4
So... is there a way to do this without using those elements? I just want to manipulate the image data and then pass it back to the main thread in a usable form. I don't really care about the DOM elements themselves, but I'm not sure if the JS API gives me any other options?
My current resize method, which requires access to DOM elements:
resizeImage: function(file, width, height, callback) {
var reader = new FileReader();
reader.onload = function (fileLoadEvent) {
var img = new Image();
img.onload = function() {
var srcX = 0, srcY = 0, srcWidth = img.width, srcHeight = img.height, ratio=1;
var srcRatio = img.width/img.height;
var dstRatio = width/height;
if(srcRatio < dstRatio) {
ratio = img.width/width;
srcHeight = height*ratio;
srcY = (img.height-srcHeight)/2;
} else {
ratio = img.height/height;
srcWidth = width*ratio;
srcX = (img.width-srcWidth)/2;
}
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(this, srcX, srcY, srcWidth, srcHeight, 0, 0, width, height);
callback(canvas);
};
img.src = reader.result;
};
reader.readAsDataURL(file);
},
Related
I'm trying to use the Incode functionality to resize an image in LogicApps.
I'm not sure if this is possible with the absence of HTML.
var inputImage = workflowContext.actions.GetFileContent.outputs.body.$content;
function resize_image(imagesrc)
{
let image = new Image();
var base = "data:image/png;base64,";
image.src = base.concat(imagesrc);
image.onload = () => {
let width = image.width;
let height = image.height;
let canvas = document.createElement('canvas');
canvas.width = newWidth ;
canvas.height = newHeight;
let context = canvas.getContext('2d');
context.drawImage(image, 450, 0, newWidth-500, newHeight);
}
return image;
}
return resize_image(inputImage);
The error I receive
The inline code action 'JavaScriptCode' execution failed, with error 'Image is not defined'.
$content is the image in Base64, for example, starts like this:
iVBORw0KGgoAAAANSUhEUgAACFwAAAMMCAYAAABkSiF3...
Inline code can only perform some simple JavaScript operations, it may not be able to install canvas.
You can create an Azure Function App to resize your image.
For more details, you can refer to Call functions from Azure Logic Apps.
I'm getting an image file from an input element, which I then catch in a vanilla javascript snippet via e.target.files[0]. Next, I'm attempting to standardize the dimensions of this image (in case it exceeds the benchmarks) via passing e.target.files[0] to source_img in the function below:
function process_img(source_img,new_width, new_height, mime_type, quality){
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);
return dataURItoBlob(canvas.toDataURL(mime_type,quality/100),mime_type);
}
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 });
}
This is currently giving me the following error:
TypeError: Argument 1 of CanvasRenderingContext2D.drawImage could not
be converted to any of: HTMLImageElement, HTMLCanvasElement,
HTMLVideoElement, ImageBitmap.
My question is two-fold:
1) Why am I getting the error and what's the most efficient way to solve it?
2) What's the most efficient way to accomplish this image resizing/processing? E.g. ways to circumvent the base64 string (while ensuring cross-browser compatibility)? Essentially I want to know how the industry pros do it for scalable projects.
I'd prefer answers with illustrative examples (along with explanations).
Note: Professional server-side dev here who's a JS newbie. Let's stick to pure JS for the scope of this question. JQuery's on my radar, but I won't touch it till I learn JS fundamentals.
question 1:change your process_img function to this:
function process_img(source_img, new_width, new_height, mime_type, quality) {
var canvas = document.createElement('canvas');
canvas.width = new_width;
canvas.height = new_height;
var ctx = canvas.getContext("2d");
var reader = new FileReader();
var img = document.createElement('img');
reader.onload = function() {
img.src = this.result;
document.body.appendChild(img); //maybe
img.style.display = 'none'; //maybe
img.onload = function() {//better
ctx.drawImage(img, 0, 0, new_width, new_height);
return dataURItoBlob(canvas.toDataURL(mime_type, quality / 100), mime_type);
};
};
reader.readAsDataURL(source_img);
}
the principle is transform your imageFile to HTMLimgElement with src by FileReader;
I would like to create a strip of images and compose a new image, like image = [image0-image1-image2].
We'll use:
images = ['https://upload.wikimedia.org/wikipedia/commons/5/55/Al-Farabi.jpg',
'https://upload.wikimedia.org/wikipedia/commons/e/e1/FullMoon2010.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/3D_coordinate_system.svg/10000px-3D_coordinate_system.svg.png']
I would like to take external above, and make a collage.
I would like to do it in background.
I learnt that is possible to use a canvas element off the dom; for the sake of watching what I am doing, I will use a canvas element here.
// create an off-screen canvas using document.createElement('canvas')
// here I use a canvas in DOM cause I cannot find a way to displayed the final collage
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
// set its dimension to target size
canvas.width = 1200;
canvas.height = 630;
and found three different behaviors for what I think should give same result. Could you explain me why?
If I manually copy and paste in console code for each image, one at a timeenter code here`
var image = new Image();
// i is the i-th element in images
image.src = images[i];
image.onload = function() {
context.save();
context.drawImage(image, canvas.width * 0.3 * i, 0, canvas.width*0.3, canvas.height);
}
I can see the elements are positioned one aside of the other, like I would like to have.
But If I copy all of three pieces of code at once, either in a loop, I can see only the last image placed in all of the three different positions:
for (var i = images.length; i <= 0; i++) {
var image = new Image();
image.src = images[i];
image.onload = function(){
context.save();
context.drawImage(image, canvas.width*0.3 * i, 0, canvas.width*0.3, canvas.height);
}
}
So I thought, maybe it's a matter of using a callback after image is loaded - I tried the following but nothing happens: canvas stays empty.
// my callback
function addImage(image, position){
image.onload = function(){
context.save();
context.drawImage(image, canvas.width*0.3 * position, 0, canvas.width*0.3, canvas.height);
}
}
function loadImages (images, callback) {
for (var i = images.length-1; i >= 0; i--) {
var image = new Image();
image.src = images[i];
callback(image, i);
}
}
// canvas will stay empty:
loadImages(images, addImage);
Can you help in clarifying the differences in the three parts, and figure out how to combine an array of images in a single one?
Possibly in background, I want to then save the image and post it via ajax.
In your loop example, all the onload functions are sharing the same i and image variables from the loop. But the onload functions are callback functions that get called after the loop completes. Thus, all the onload functions are using the same i and image values from after the loop completed. You need to create a local scope such that each onload function has its own i and image values. For example...
for (var i = 0; i < images.length; i++) {
var image = new Image();
image.src = images[i];
image.onload = function(image, i) {
return function(){
context.drawImage(image, canvas.width*0.3 * i, 0, canvas.width*0.3, canvas.height);
}
}(image, i);
}
what i Need
i need to scan and check image is nude or not.
i using nude.js for image detection problem im facing after including nude.js when page is loaded the error appear in console.
nude.js
(function(){
var nude = (function(){
// private var definition
var canvas = null,
ctx = null,
resultFn = null,
// private functions
initCanvas = function(){
canvas = document.createElement("canvas");
// the canvas should not be visible
canvas.style.display = "none";
var b = document.getElementsByTagName("body")[0];
b.appendChild(canvas);
ctx = canvas.getContext("2d");
},
loadImageById = function(id){
// get the image
var img = document.getElementById(id);
// apply the width and height to the canvas element
canvas.width = img.width;
canvas.height = img.height;
// reset the result function
resultFn = null;
// draw the image into the canvas element
ctx.drawImage(img, 0, 0);
},
scanImage = function(){
// get the image data
var image = ctx.getImageData(0, 0, canvas.width, canvas.height),
imageData = image.data;
var myWorker = new Worker('worker.nude.js'),
message = [imageData, canvas.width, canvas.height];
myWorker.postMessage(message);
myWorker.onmessage = function(event){
resultHandler(event.data);
}
},
abc.html
<script>
$(document).ready(function(){
$("img").load(function(){
nude.load('testImage');
nude.scan(function(result)
{
if(!result) alert("no nude");
else
alert("nude)"); });
});
});
Error
Uncaught TypeError: Cannot read property 'width' of null
canvas.width = img.width; // line 28
You need to call nude.init();. I faced the same problem today. Even if nude.js is calling itself a nude.init() after registering the nude object to the window - but it doesn't seem to happen.
Side Note: the canvas itself is null. The error comes from the canvas, not from the img.
Include jQuery and call $(document).ready(...) and do a nude.init(); in there.
I'm currently attempting to dynamically retrieve the location of an image and load it into a html5 canvas. The canvas is in a bootstrap responsive environment hence I never know the actual width of the canvas until the document is ready. Using the code below I've managed to resize the image and canvas to what I require but the image quality is very poor. I'm thinking it is because of the vast reduction in size but again unfortunately I have no control over the size of the original image uploaded.
function load_canvas_image(photo_type, vehicle_index) {
image_location = get_image_location(photo_type, vehicle_index);
var img = new Image();
img.src = image_location;
var canvas = $("#myCanvas")[0];
var canvas_context = canvas.getContext("2d");
canvas_width = canvas.width;
canvas_height = canvas.height;
img.onload = function() {
cur_height = img.height;
cur_width = img.width;
aspect_ratio = cur_width/canvas_width;
new_height = cur_height/aspect_ratio;
canvas.height = new_height;
canvas_context.drawImage(img, 0, 0, canvas.width, canvas.height);
}
};
$(document).on("change", "#add_damage_location", function() {
load_canvas_image($(this).val(), $('#vehicle_index').val());
});
I'm hoping someone knows how to use a similar method to reduce the width and height of an image using a similar method to the above whilst keeping the quality of the image as good as possible, I know to expect some reduction in quality but in it's current state it is unusable.
Thanks in Advance