web-dev-qa-db-fra.com

Redimensionner l'image avec une toile javascript (en douceur)

J'essaie de redimensionner certaines images avec du canevas, mais je ne sais pas comment les lisser . Sur Photoshop, les navigateurs, etc., ils utilisent quelques algorithmes (par exemple, bicubique, bilinéaire) mais je ne sais pas. si ceux-ci sont construits dans la toile ou pas.

Voici mon violon: 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);

Le premier est une balise d'image redimensionnée normale, et le second est un canevas. Remarquez comment la toile n'est pas aussi lisse. Comment puis-je atteindre la "douceur"?

64
steve

Vous pouvez utiliser la réduction progressive pour obtenir de meilleurs résultats. La plupart des navigateurs semblent utiliser une interpolation linéaire plutôt que bi-cubique } lors du redimensionnement des images.

(Update Une propriété de qualité a été ajoutée à la spécification, imageSmoothingQuality actuellement disponible dans Chrome uniquement.)

À moins que vous ne choisissiez pas de lissage ou le voisin le plus proche, le navigateur interpolera toujours l'image après l'avoir réduite en tant que filtre passe-bas pour éviter les alias.

La bi-linéaire utilise 2x2 pixels pour effectuer l'interpolation, tandis que la bi-cubique utilise la méthode 4x4. Ainsi, en procédant par étapes, vous pouvez vous rapprocher du résultat bi-cubique tout en utilisant une interpolation bi-linéaire comme dans les images résultantes.

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>

En fonction de l'ampleur de votre redimensionnement, vous pouvez ignorer l'étape 2 si la différence est moindre.

Dans la démo, vous pouvez voir que le nouveau résultat est maintenant très similaire à l'élément image.

102
user1693593

J'ai créé un service Angular réutilisable pour gérer le redimensionnement d'images et de toiles de haute qualité pour les personnes intéressées: https://Gist.github.com/transitive-bullshit/37bac5e741eaec60e983

Le service comprend deux solutions car ils ont tous les deux leur propre avantage/inconvénient. L’approche par convolution de Lanczos est de meilleure qualité au prix de ralentissement, alors que l’approche de réduction progressive conduit à des résultats raisonnablement anti-aliasés et est nettement plus rapide.

Exemple d'utilisation:

angular.module('demo').controller('ExampleCtrl', function (imageService) {
  // EXAMPLE USAGE
  // NOTE: it's 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
    })
})
16
fisch2

Puisque le violon de le violon de Trung Le Nguyen Nhat n’est pas correct du tout (Il utilise simplement l’image originale dans la dernière étape)
J'ai écrit mon propre violon général avec comparaison des performances:

VIOLON

En gros c'est:

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);
}
8
Sebastian Ott

J'ai créé une bibliothèque qui vous permet de réduire n'importe quel pourcentage tout en conservant toutes les données de couleur.

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

Ce fichier que vous pouvez inclure dans le navigateur. Les résultats ressembleront à ceux de photoshop ou de magick d'images, préservant toutes les données de couleur, établissant une moyenne des pixels, plutôt que de prendre ceux qui se trouvent à proximité et de supprimer les autres. Il n'utilise pas de formule pour deviner les moyennes, mais prend la moyenne exacte.

4
Funkodebat

J'ai écrit un petit utilitaire js pour recadrer et redimensionner une image sur un serveur Voici link sur le projet GitHub. Aussi, vous pouvez obtenir une goutte de l'image finale pour l'envoyer.

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
Diyaz Yakubov

Sur la base de la réponse K3N, je réécris le code en général pour tous

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);

UPDATE JSFIDDLE DEMO

Voici mon ONLINE DEMO

1

Certains de ces extraits de code sont courts et fonctionnent, mais ils ne sont pas faciles à suivre et à comprendre.

Comme je ne suis pas un partisan du "copier-coller" du stack-overflow, je voudrais que les développeurs comprennent le code qu’ils utilisent. Poussez dans leur logiciel, j'espère que vous trouverez le texte ci-dessous utile.

DEMO: Redimensionnement des images avec le fiddler JS et HTML Canvas Demo.

Vous pouvez trouver 3 méthodes différentes pour faire ce redimensionnement, ce qui vous aidera à comprendre comment fonctionne le code et pourquoi.

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

Le code complet de la démonstration et de la méthode TypeScript que vous souhaitez utiliser dans votre code se trouve dans le projet GitHub.

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

C'est le code final:

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
        }
    };
}}
0
Eyal c