Resize image using javascript canvas (seamlessly)

I'm trying to resize some images using canvas, but I don't know how to smooth them. In Photoshop, browsers, etc. There are several algorithms that they use (for example, bicubic, bilinear), but I do not know if they are embedded in the canvas or not.

Here is my fiddle: http://jsfiddle.net/EWupT/

var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width=300 canvas.height=234 ctx.drawImage(img, 0, 0, 300, 234); document.body.appendChild(canvas); 

The first is a regular resized image tag, and the second is a canvas. Please note that canvas alone is not so smooth. How do I achieve smoothness?

+71
javascript html5 image html5-canvas canvas
Oct 09 '13 at 2:41
source share
7 answers

You can use the lowering step to achieve better results. Most browsers seem to use linear interpolation rather than bicubic when resizing images.

( Update The imageSmoothingQuality quality imageSmoothingQuality which is currently only available in Chrome, has been added to the specification.)

If the smoothing mode or the nearest neighbor is not selected, the browser will always interpolate the image after it is reduced, since this function is used as a low-pass filter to avoid overlapping.

The bilinear uses 2x2 pixels for interpolation, while the bi-cubic uses 4x4, so by doing this in stages, you can get closer to the bi-cubic result using bilinear interpolation, as seen in the images.

 var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var img = new Image(); img.onload = function () { // set size proportional to image canvas.height = canvas.width * (img.height / img.width); // step 1 - resize to 50% var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width * 0.5; oc.height = img.height * 0.5; octx.drawImage(img, 0, 0, oc.width, oc.height); // step 2 octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5); // step 3, resize to final size ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height); } img.src = "//i.imgur.com/SHo6Fub.jpg"; 
 <img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234"> <canvas id="canvas" width=300></canvas> 

Depending on how sharp your size is, you can skip step 2 if the difference is smaller.

In the demo, you can see that the new result is now very similar to the image element.

+113
09 Oct '13 at 3:08 on
source share

I created a reusable Angular service for high-quality image / canvas resizing for anyone interested: https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983

The service includes two solutions, because they both have their pros / cons. Lanczos's convolutional approach is better due to the fact that it is slower, while the phased approach to downscaling gives reasonably smooth results and much faster.

Usage example:

 angular.module('demo').controller('ExampleCtrl', function (imageService) { // EXAMPLE USAGE // NOTE: it bad practice to access the DOM inside a controller, // but this is just to show the example usage. // resize by lanczos-sinc filter imageService.resize($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) // resize by stepping down image size in increments of 2x imageService.resizeStep($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) }) 
+17
Sep 16 '14 at 23:34
source share

Since the Trung Le Nguyen Nhat violin is not quite correct (it just uses the original image in the last step)
I wrote my general fiddle with a performance comparison:

Fiddle

Mainly:

 img.onload = function() { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"), oc = document.createElement('canvas'), octx = oc.getContext('2d'); canvas.width = width; // destination canvas size canvas.height = canvas.width * img.height / img.width; var cur = { width: Math.floor(img.width * 0.5), height: Math.floor(img.height * 0.5) } oc.width = cur.width; oc.height = cur.height; octx.drawImage(img, 0, 0, cur.width, cur.height); while (cur.width * 0.5 > width) { cur = { width: Math.floor(cur.width * 0.5), height: Math.floor(cur.height * 0.5) }; octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height); } ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height); } 
+15
Sep 22 '16 at 11:31
source share

I created a library that allows you to omit any percentage while preserving all color data.

https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

This file can be included in the browser. The results will look like photoshop or image magic, preserving all color data, averaging pixels, instead of taking nearby ones and throwing others. He does not use the formula for guessing average values, it takes an exact average value.

+4
Jul 30 '14 at 20:00
source share

Based on K3N's answer, I rewrite the code in general for those who want

 var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width; oc.height = img.height; octx.drawImage(img, 0, 0); while (oc.width * 0.5 > width) { oc.width *= 0.5; oc.height *= 0.5; octx.drawImage(oc, 0, 0, oc.width, oc.height); } oc.width = width; oc.height = oc.width * img.height / img.width; octx.drawImage(img, 0, 0, oc.width, oc.height); 

JSFIDDLE DEMO UPDATE

Here is my ONLINE DEMO

+3
Jun 07 '16 at 10:09
source share

Although some of these code snippets are short and work, they are not so easy to understand and understand.

Since I am not a fan of copy-paste from, I would like the developers to understand the code that they embed in their software, I hope you find the following useful.

DEMO : resizing images using JS and HTML Canvas Demo fiddler.

You can find 3 different resizing methods to help you understand how the code works and why.

https://jsfiddle.net/1b68eLdr/93089/

The full demo code and TypeScript method that you can use in your code can be found in the GitHub project.

https://github.com/eyalc4/ts-image-resizer

This is the final code:

 export class ImageTools { base64ResizedImage: string = null; constructor() { } ResizeImage(base64image: string, width: number = 1080, height: number = 1080) { let img = new Image(); img.src = base64image; img.onload = () => { // Check if the image require resize at all if(img.height <= height && img.width <= width) { this.base64ResizedImage = base64image; // TODO: Call method to do something with the resize image } else { // Make sure the width and height preserve the original aspect ratio and adjust if needed if(img.height > img.width) { width = Math.floor(height * (img.width / img.height)); } else { height = Math.floor(width * (img.height / img.width)); } let resizingCanvas: HTMLCanvasElement = document.createElement('canvas'); let resizingCanvasContext = resizingCanvas.getContext("2d"); // Start with original image size resizingCanvas.width = img.width; resizingCanvas.height = img.height; // Draw the original image on the (temp) resizing canvas resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height); let curImageDimensions = { width: Math.floor(img.width), height: Math.floor(img.height) }; let halfImageDimensions = { width: null, height: null }; // Quickly reduce the dize by 50% each time in few iterations until the size is less then // 2x time the target size - the motivation for it, is to reduce the aliasing that would have been // created with direct reduction of very big image to small image while (curImageDimensions.width * 0.5 > width) { // Reduce the resizing canvas by half and refresh the image halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5); halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5); resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, halfImageDimensions.width, halfImageDimensions.height); curImageDimensions.width = halfImageDimensions.width; curImageDimensions.height = halfImageDimensions.height; } // Now do final resize for the resizingCanvas to meet the dimension requirments // directly to the output canvas, that will output the final image let outputCanvas: HTMLCanvasElement = document.createElement('canvas'); let outputCanvasContext = outputCanvas.getContext("2d"); outputCanvas.width = width; outputCanvas.height = height; outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, width, height); // output the canvas pixels as an image. params: format, quality this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85); // TODO: Call method to do something with the resize image } }; }} 
+2
Dec 31 '19 at 10:16
source share

I wrote a small js utility for cropping and resizing images on the interface. Here is the link in the GitHub project. You can also get a blob from the final image to send it.

 import imageSqResizer from './image-square-resizer.js' let resizer = new imageSqResizer( 'image-input', 300, (dataUrl) => document.getElementById('image-output').src = dataUrl; ); //Get blob let formData = new FormData(); formData.append('files[0]', resizer.blob); //get dataUrl document.getElementById('image-output').src = resizer.dataUrl; 
+1
Nov 14 '17 at 13:42 on
source share



All Articles