web-dev-qa-db-fra.com

Redimensionnement d'une image dans une toile HTML5

J'essaie de créer une vignette côté client à l'aide de javascript et d'un élément de structure, mais lorsque je réduis l'image, elle semble affreuse. Il semble que cela ait été réduit dans photoshop avec le rééchantillonnage réglé sur "Le plus proche voisin" au lieu de Bicubic. Je sais qu'il est possible d'obtenir un résultat correct, car ce site peut très bien le faire en utilisant également un canevas. J'ai essayé d'utiliser le même code que celui indiqué dans le lien "[Source]", mais il a toujours l'air terrible. Y a-t-il quelque chose qui me manque, un paramètre qui doit être défini ou quelque chose?

MODIFIER:

J'essaye de redimensionner un jpg. J'ai essayé de redimensionner le même jpg sur le site lié et dans Photoshop, et il a l'air bien lorsqu'il est réduit.

Voici le code pertinent:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2:

Il semble que je me suis trompé, le site Web lié ne faisait pas mieux pour réduire l’image. J'ai essayé les autres méthodes suggérées et aucune d'entre elles n'a l'air meilleure. Voici ce que les différentes méthodes ont abouti:

Photoshop:

alt text

Toile:

alt text

Image avec rendu d'image: optimiséQuality réglé et mis à l'échelle avec largeur/hauteur:

alt text

Image avec rendu d'image: optimiséQuality défini et mis à l'échelle avec -moz-transform:

alt text

Toile redimensionner sur pixastic:

alt text

Je suppose que cela signifie que firefox n’utilise pas l’échantillonnage bicubique comme il est supposé le faire. Je vais devoir attendre jusqu'à ce qu'ils l'ajoutent.

EDIT3:

Image originale

307
Telanor

Alors, que faites-vous si tous les navigateurs (en fait, Chrome 5 m'en ont fourni un très bon) ne vous donneront pas une qualité de rééchantillonnage suffisante? Vous les implémentez vous-même alors! Oh, allez, nous entrons dans la nouvelle ère du Web 3.0, navigateurs compatibles HTML5, compilateurs javascript JIT super optimisés, machines multicœurs (†), avec beaucoup de mémoire, de quoi avez-vous peur? Hé, il y a le mot Java en javascript, donc cela devrait garantir la performance, non? Voici le code générateur de vignettes:

// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function(x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1;
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width : sx,
        height : Math.round(img.height * sx / img.width),
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(this.process1, 0, this, 0);
}

thumbnailer.prototype.process1 = function(self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
                            + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
};

... avec lequel vous pouvez produire des résultats comme ceux-ci!

img717.imageshack.us/img717/8910/lanczos358.png

de toute façon, voici une version "corrigée" de votre exemple:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

Il est maintenant temps de présenter vos meilleurs navigateurs et de voir lequel augmentera le moins la tension artérielle de votre client!

Umm, où est ma balise de sarcasme?

(car de nombreuses parties du code sont basées sur Anrieff Gallery Generator est-il également couvert par la GPL2? Je ne sais pas)

en fait, du fait de la limitation de javascript, le multicœur n'est pas pris en charge.

388
syockit

Algorithme de redimensionnement/rééchantillonnage d'image rapide utilisant un filtre Hermite avec JavaScript. Soutenir la transparence, donne de bonne qualité. Aperçu:

enter image description here

Mise à jour : version 2.0 ajoutée sur GitHub (plus rapide, travailleurs Web + objets transférables). Enfin je l'ai fait travailler!

Git: https://github.com/viliusle/Hermite-resize
Démo: http://viliusle.github.io/miniPaint/

/**
 * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
 * 
 * @param {HtmlElement} canvas
 * @param {int} width
 * @param {int} height
 * @param {boolean} resize_canvas if true, canvas will be resized. Optional.
 */
function resample_single(canvas, width, height, resize_canvas) {
    var width_source = canvas.width;
    var height_source = canvas.height;
    width = Math.round(width);
    height = Math.round(height);

    var ratio_w = width_source / width;
    var ratio_h = height_source / height;
    var ratio_w_half = Math.ceil(ratio_w / 2);
    var ratio_h_half = Math.ceil(ratio_h / 2);

    var ctx = canvas.getContext("2d");
    var img = ctx.getImageData(0, 0, width_source, height_source);
    var img2 = ctx.createImageData(width, height);
    var data = img.data;
    var data2 = img2.data;

    for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
            var x2 = (i + j * width) * 4;
            var weight = 0;
            var weights = 0;
            var weights_alpha = 0;
            var gx_r = 0;
            var gx_g = 0;
            var gx_b = 0;
            var gx_a = 0;
            var center_y = (j + 0.5) * ratio_h;
            var yy_start = Math.floor(j * ratio_h);
            var yy_stop = Math.ceil((j + 1) * ratio_h);
            for (var yy = yy_start; yy < yy_stop; yy++) {
                var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
                var center_x = (i + 0.5) * ratio_w;
                var w0 = dy * dy; //pre-calc part of w
                var xx_start = Math.floor(i * ratio_w);
                var xx_stop = Math.ceil((i + 1) * ratio_w);
                for (var xx = xx_start; xx < xx_stop; xx++) {
                    var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
                    var w = Math.sqrt(w0 + dx * dx);
                    if (w >= 1) {
                        //pixel too far
                        continue;
                    }
                    //hermite filter
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    var pos_x = 4 * (xx + yy * width_source);
                    //alpha
                    gx_a += weight * data[pos_x + 3];
                    weights_alpha += weight;
                    //colors
                    if (data[pos_x + 3] < 255)
                        weight = weight * data[pos_x + 3] / 250;
                    gx_r += weight * data[pos_x];
                    gx_g += weight * data[pos_x + 1];
                    gx_b += weight * data[pos_x + 2];
                    weights += weight;
                }
            }
            data2[x2] = gx_r / weights;
            data2[x2 + 1] = gx_g / weights;
            data2[x2 + 2] = gx_b / weights;
            data2[x2 + 3] = gx_a / weights_alpha;
        }
    }
    //clear and resize canvas
    if (resize_canvas === true) {
        canvas.width = width;
        canvas.height = height;
    } else {
        ctx.clearRect(0, 0, width_source, height_source);
    }

    //draw
    ctx.putImageData(img2, 0, 0);
}
36
ViliusL

Essayez pica - c'est un resizer hautement optimisé avec des algorithmes sélectionnables. Voir démo .

Par exemple, l’image originale du premier message est redimensionnée en 120 ms avec le filtre Lanczos et la fenêtre 3px ou 60ms avec le filtre Boîte et la fenêtre 0,5px. Pour un format d'image élevé de 17 Mo, le redimensionnement de l'image 5000x3000px prend environ 1 seconde sur le bureau et 3 secondes sur le mobile.

Tous les principes de redimensionnement ont été très bien décrits dans ce fil, et pica n’ajoute rien à la science de la fusée. Mais il est très bien optimisé pour les JIT-s modernes et est prêt à l’emploi (par npm ou bower). En outre, il utilise des travailleurs Web lorsqu'ils sont disponibles pour éviter le blocage de l'interface.

Je prévois également d'ajouter bientôt le support des masques flous, car il est très utile après une réduction d'échelle.

24
Vitaly

Je sais que c’est un vieux fil conducteur, mais il pourrait être utile pour certaines personnes, comme moi, que des mois plus tard s’attaquent à ce problème pour la première fois.

Voici un code qui redimensionne l'image à chaque fois que vous rechargez l'image. Je suis conscient que ce n'est pas optimal du tout, mais je le fournis comme une preuve de concept.

Aussi, désolé d'utiliser jQuery pour des sélecteurs simples, mais je me sens trop à l'aise avec la syntaxe.

$(document).on('ready', createImage);
$(window).on('resize', createImage);

var createImage = function(){
  var canvas = document.getElementById('myCanvas');
  canvas.width = window.innerWidth || $(window).width();
  canvas.height = window.innerHeight || $(window).height();
  var ctx = canvas.getContext('2d');
  img = new Image();
  img.addEventListener('load', function () {
    ctx.drawImage(this, 0, 0, w, h);
  });
  img.src = 'http://www.ruinvalor.com/Telanor/images/original.jpg';
};
html, body{
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #000;
}
canvas{
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas Resize</title>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

Ma fonction createImage est appelée une fois lorsque le document est chargé, puis à chaque fois que la fenêtre reçoit un événement de redimensionnement.

Je l'ai testé sous Chrome 6 et Firefox 3.6, tous deux sur Mac. Cette "technique" consomme du processeur comme si c'était de la glace en été, mais ça marche.

14
cesarsalazar

J'ai mis en place des algorithmes d'interpolation d'images sur des tableaux de pixels en canevas HTML qui pourraient être utiles ici:

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

Celles-ci peuvent être copiées/collées et peuvent être utilisées à l'intérieur de travailleurs Web pour redimensionner des images (ou toute autre opération nécessitant une interpolation - je les utilise pour les images actuelles).

Je n’ai pas ajouté la substance lanczos ci-dessus, alors n'hésitez pas à ajouter cela à titre de comparaison si vous le souhaitez.

9
Daniel

Je vous suggère fortement de vérifier ce lien et assurez-vous qu'il est défini sur true.

Contrôle du comportement de la mise à l'échelle de l'image

Introduit dans Gecko 1.9.2 (Firefox 3.6/Thunderbird 3.1/Fennec 1.0)

Gecko 1.9.2 a introduit la propriété mozImageSmoothingEnabled dans l'élément canvas. si cette valeur booléenne est false, les images ne seront pas lissées lors de leur mise à l'échelle. Cette propriété est vraie par défaut. voir en clair?

  1. cx.mozImageSmoothingEnabled = false;
6
Evan Carroll

Il s'agit d'une fonction javascript adaptée du code de @ Telanor. Lorsque vous transmettez une image base64 en tant que premier argument à la fonction, elle renvoie la base64 de l'image redimensionnée. maxWidth et maxHeight sont facultatifs.

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) === 'undefined') var maxWidth = 500;
  if(typeof(maxHeight) === 'undefined') var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}
6
Christophe Marois

Si vous essayez simplement de redimensionner une image, je vous recommande de définir width et height de l'image avec CSS. Voici un exemple rapide:

_.small-image {
    width: 100px;
    height: 100px;
}
_

Notez que les variables height et width peuvent également être définies à l'aide de JavaScript. Voici un exemple de code rapide:

_var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you'll confuse IE
_

De plus, pour que l’image redimensionnée paraisse bien, ajoutez les règles CSS suivantes au sélecteur d’image:

Autant que je sache, tous les navigateurs, à l'exception de IE, utilisent un algorithme bicubique pour redimensionner les images par défaut. Par conséquent, vos images redimensionnées devraient bien paraître dans Firefox et Chrome.

Si le réglage de _ width et height de css ne fonctionne pas, vous pouvez jouer avec un css transform:

Si pour une raison quelconque vous avez besoin d’utiliser un canevas, veuillez noter qu’une image peut être redimensionnée de deux manières: en redimensionnant le canevas avec css ou avec dessiner l'image à une taille plus petite.

Voir cette question pour plus de détails.

6
Xavi

j'ai obtenu cette image en faisant un clic droit sur l'élément de toile dans Firefox et en enregistrant en tant que.

alt text

var img = new Image();
img.onload = function () {
    console.debug(this.width,this.height);
    var canvas = document.createElement('canvas'), ctx;
    canvas.width = 188;
    canvas.height = 150;
    document.body.appendChild(canvas);
    ctx = canvas.getContext('2d');
    ctx.drawImage(img,0,0,188,150);
};
img.src = 'original.jpg';

de toute façon, voici une version "corrigée" de votre exemple:

var img = new Image();
// added cause it wasnt defined
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
// adding it to the body

document.body.appendChild(canvasCopy);

var copyContext = canvasCopy.getContext("2d");

img.onload = function()
{
        var ratio = 1;

        // defining cause it wasnt
        var maxWidth = 188,
            maxHeight = 150;

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
};

// changed for example
img.src = 'original.jpg';
4
robert

Pour redimensionner une image avec une largeur inférieure à l'original, j'utilise:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

et ça marche =).

4
Yaffle

Le problème avec certaines de ces solutions est qu’elles accèdent directement aux données de pixels et les parcourent en boucle pour effectuer le sous-échantillonnage. En fonction de la taille de l'image, cela peut demander beaucoup de ressources et il serait préférable d'utiliser les algorithmes internes du navigateur.

La fonction drawImage () utilise une méthode de ré-échantillonnage par interpolation linéaire et plus proche voisin. Cela fonctionne bien lorsque vous ne redimensionnez pas plus de la moitié de la taille d'origine .

Si vous ne redimensionnez que de moitié à la fois, les résultats seront plutôt bons et beaucoup plus rapides que l’accès aux données de pixels.

Cette fonction permet de réduire l’échantillon de moitié à la fois jusqu’à atteindre la taille souhaitée:

  function resize_image( src, dst, type, quality ) {
     var tmp = new Image(),
         canvas, context, cW, cH;

     type = type || 'image/jpeg';
     quality = quality || 0.92;

     cW = src.naturalWidth;
     cH = src.naturalHeight;

     tmp.src = src.src;
     tmp.onload = function() {

        canvas = document.createElement( 'canvas' );

        cW /= 2;
        cH /= 2;

        if ( cW < src.width ) cW = src.width;
        if ( cH < src.height ) cH = src.height;

        canvas.width = cW;
        canvas.height = cH;
        context = canvas.getContext( '2d' );
        context.drawImage( tmp, 0, 0, cW, cH );

        dst.src = canvas.toDataURL( type, quality );

        if ( cW <= src.width || cH <= src.height )
           return;

        tmp.src = dst.src;
     }

  }
  // The images sent as parameters can be in the DOM or be image objects
  resize_image( $( '#original' )[0], $( '#smaller' )[0] );

Crédits à ce post

3
Jesús Carrera

J'ai l'impression que le module que j'ai écrit produira des résultats similaires à ceux de photoshop, car il préserve les données de couleur en les faisant la moyenne, sans appliquer d'algorithme. C'est un peu lent, mais pour moi c'est le meilleur, car il préserve toutes les données de couleur.

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

Il ne faut pas prendre le voisin le plus proche et laisser tomber d'autres pixels, ou échantillonner un groupe et prendre une moyenne aléatoire. Il prend la proportion exacte que chaque pixel source doit produire dans le pixel de destination. La couleur de pixel moyenne dans la source sera la couleur de pixel moyenne dans la destination, ce que ces autres formules, je pense qu'elles ne seront pas.

un exemple d'utilisation est au bas de https://github.com/danschumann/limby-resize

UPDATE OCT 2018 : Ces jours-ci, mon exemple est plus académique que toute autre chose. Webgl est à peu près 100%, il serait donc préférable de redimensionner avec cela pour produire des résultats similaires, mais plus rapide. PICA.js fait cela, je crois. -

2
Funkodebat

Donc, quelque chose d’intéressant que j’ai trouvé il ya quelque temps en travaillant avec canvas peut être utile:

Pour redimensionner le contrôle de zone de travail seul, vous devez utiliser les attributs height="" et width="" (ou canvas.width/canvas.height.). Si vous utilisez CSS pour redimensionner le canevas, il va réellement étirer (c'est-à-dire redimensionner) le contenu du canevas pour qu'il corresponde à tout le canevas (au lieu d'augmenter ou de diminuer simplement la surface du canevas.

Il serait intéressant d'essayer de dessiner l'image dans un contrôle de zone de dessin avec les attributs hauteur et largeur définis à la taille de l'image, puis d'utiliser CSS pour redimensionner la zone de travail à la taille souhaitée. Peut-être que cela utiliserait un algorithme de redimensionnement différent.

Il convient également de noter que la toile a des effets différents selon les navigateurs (et même les différentes versions des différents navigateurs). Les algorithmes et techniques utilisés dans les navigateurs sont susceptibles de changer au fil du temps (notamment avec Firefox 4 et Chrome 6 qui sortent si tôt, ce qui mettra fortement l'accent sur les performances de rendu sur les toiles).

En outre, vous voudrez peut-être essayer SVG, car il utilise probablement un algorithme différent.

Bonne chance!

2
mattbasta

J'ai converti la réponse de @ syockit ainsi que l'approche progressive en un service réutilisable Angular pour toute personne intéressée: https://Gist.github.com/fisch0920/37bac5e741eaec60e98

J'ai inclus les deux solutions car elles ont toutes deux leur propre avantage/inconvénient. L’approche par convolution de Lanczos est de meilleure qualité au prix de ralentissement, tandis 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
    })
})
1
fisch2

Resizer d'image Javascript rapide et simple:

https://github.com/calvintwr/Hermite-resize

Utilisation:

h.resize({
    source: document.getElementById('image'), // any canvas or image elements, jQuery or native
    width: 400,
    height: 600,
    output: 'image', // [optional] `image` or `canvas`. If not entered output is same as input element.
    quality: 0.7, // [optional] applicable for `image` output only
}, function(output) {
    //your callback
});

Histoire

C’est vraiment après de nombreuses recherches, lectures et tentatives.

L'algorithme de resizer utilise le script Hermite de @ ViliusL (le resizer d'Hermite est vraiment le plus rapide et donne un bon résultat). Étendu avec les fonctionnalités dont vous avez besoin.

Déclare 1 travailleur pour effectuer le redimensionnement afin qu'il ne gèle pas votre navigateur lors du redimensionnement, contrairement à tous les autres redimensionneurs JS.

1
Calvintwr

Merci @syockit pour une réponse géniale. cependant, j'ai dû reformater un peu comme suit pour que cela fonctionne. Peut-être en raison de problèmes de numérisation DOM:

$(document).ready(function () {

$('img').on("load", clickA);
function clickA() {
    var img = this;
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 50, 3);
    document.body.appendChild(canvas);
}

function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width: sx,
        height: Math.round(img.height * sx / img.width)
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(process1, 0, this, 0);
}

//returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function (x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    }
}

process1 = function (self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(process1, 0, self, u);
    else
        setTimeout(process2, 0, self);
};

process2 = function (self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
}
});
0
Manish