139 votes

Redimensionner l'image avec javascript canvas (en douceur)

Je suis en train d'essayer de redimensionner certaines images avec canvas mais je ne sais pas comment les adoucir. Sur photoshop, les navigateurs, etc., il y a quelques algorithmes qu'ils utilisent (par exemple, bicubic, bilinéaire) mais je ne sais pas si ceux-ci sont intégrés à canvas ou non.

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

Le premier est une balise d'image redimensionnée normale, et le deuxième est canvas. Remarquez comment celui de canvas n'est pas aussi lisse. Comment puis-je obtenir une 'fluidité'?

172voto

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

(Mise à jour Il a été ajouté une propriété de qualité aux spécifications, imageSmoothingQuality qui est actuellement disponible uniquement dans Chrome.)

Sauf si l'on choisit aucune lissage ou voisin le plus proche, le navigateur interpolera toujours l'image après l'avoir redimensionnée car cette fonction agit comme un filtre passe-bas pour éviter l'aliasing.

La méthode bilinéaire utilise des pixels 2x2 pour faire l'interpolation tandis que la bicubique utilise des pixels 4x4, donc en le faisant par étapes, vous pouvez obtenir un résultat proche de la méthode bicubique tout en utilisant l'interpolation bilinéaire comme on peut le voir sur 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);

    // étape 1 - redimensionner à 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);

    // étape 2
    octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);

    // étape 3, redimensionner à la taille finale
    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";

En fonction de l'importance de la redimension, vous pouvez éventuellement sauter l'étape 2 si la différence est faible.

Dans la démo, vous pouvez constater que le nouveau résultat est maintenant beaucoup plus semblable à l'élément image.

70voto

Sebastian Ott Points 701

Depuis le fiddle de Trung Le Nguyen Nhat n'est pas du tout correct (il utilise simplement l'image d'origine dans la dernière étape)
J'ai écrit mon propre fiddle général avec comparaison des performances :

FIDDLE

Essentiellement 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; // taille de destination du canvas
   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);
}

18voto

fisch2 Points 45

J'ai créé un service Angular réutilisable pour gérer le redimensionnement de haute qualité des images / canevas pour ceux qui sont intéressés : https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983

Le service propose deux solutions car chacune a ses propres avantages / inconvénients. L'approche de convolution lanczos est de meilleure qualité mais plus lente, tandis que l'approche de réduction progressive produit des résultats raisonnablement antialiasés et est significativement plus rapide.

Exemple d'utilisation :

angular.module('demo').controller('ExampleCtrl', function (serviceImage) {
  // EXEMPLE D'UTILISATION
  // REMARQUE : il est déconseillé d'accéder au DOM à l'intérieur d'un contrôleur, 
  // mais ceci est juste pour montrer l'exemple d'utilisation.

  // redimensionner avec le filtre lanczos-sinc
  serviceImage.resize($('#myimg')[0], 256, 256)
    .then(function (imageRedimensionnée) {
      // faire quelque chose avec l'image redimensionnée
    })

  // redimensionner en diminuant progressivement la taille de l'image par incréments de 2x
  serviceImage.resizeStep($('#myimg')[0], 256, 256)
    .then(function (imageRedimensionnée) {
      // faire quelque chose avec l'image redimensionnée
    })
})

12voto

Eyal c Points 357

Alors que certains de ces extraits de code sont courts et fonctionnent, ils ne sont pas triviaux à suivre et à comprendre.

Comme je ne suis pas fan du "copier-coller" depuis stack-overflow, j'aimerais que les développeurs comprennent le code qu'ils intègrent dans leur logiciel, j'espère que ce qui suit vous sera utile.

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

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

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

Le code complet des deux démos, ainsi que la méthode TypeScript que vous pouvez vouloir utiliser dans votre code, peut être trouvé dans le projet GitHub.

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

Voici 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 = () => {

        // Vérifiez si l'image nécessite un redimensionnement
        if(img.height <= height && img.width <= width) {
            this.base64ResizedImage = base64image;

            // TODO: Appeler la méthode pour faire quelque chose avec l'image redimensionnée
        }
        else {
            // Assurez-vous que la largeur et la hauteur conservent le rapport hauteur/largeur d'origine et ajustez si nécessaire
            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");

            // Commencez avec la taille d'image d'origine
            resizingCanvas.width = img.width;
            resizingCanvas.height = img.height;

            // Dessinez l'image d'origine sur le canvas de redimensionnement (temporaire)
            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
            };

            // Réduisez rapidement la taille de moitié à chaque fois en quelques itérations jusqu'à ce que la taille soit inférieure à 2 fois la taille cible - la motivation pour cela est de réduire le crénelage qui aurait été créé avec la réduction directe d'une très grande image à une petite image
            while (curImageDimensions.width * 0.5 > width) {
                // Réduisez le canvas de redimensionnement de moitié et rafraîchissez l'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;
            }

            // Faites maintenant le redimensionnement final pour que le canvas de redimensionnement réponde aux exigences de dimension
            // directement sur le canvas de sortie, qui produira l'image finale
            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);

            // exportez les pixels du canvas en tant qu'image. params: format, qualité
            this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85);

            // TODO: Appeler la méthode pour faire quelque chose avec l'image redimensionnée
        }
    };
}}

9voto

cagdas_ucar Points 87

Je ne comprends pas pourquoi personne ne suggère createImageBitmap.

createImageBitmap(
    document.getElementById('image'), 
    { resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' }
)
.then(imageBitmap => 
    document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0)
);

fonctionne magnifiquement (en supposant que vous avez défini des identifiants pour l'image et le canvas).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X