J'essaie de redimensionner une image sur un appareil mobile. (Utilisation du cadre ionique 2, basé sur angulaire 2).

Ma taille de fichier maximale est de 5 Mo et certains appareils capturent des images plus grandes que cela. J'essaye donc actuellement de redimensionner l'image en utilisant canvas.toDataURL() mais c'est extrêmement lent. (L'application ne répond pas pendant 15 à 20 secondes).

Ma fonction de redimensionnement actuelle est la suivante:

private resize(outputFormat, sourceImgObj, quality) {
    let mimeType = "image/jpeg";

    let cvs = document.createElement('canvas');
    cvs.width = sourceImgObj.naturalWidth;
    cvs.height = sourceImgObj.naturalHeight;
    let ctx = cvs.getContext("2d").drawImage(sourceImgObj, 0, 0);

    let newImageData = cvs.toDataURL(mimeType, quality/100);
    return newImageData;
}

Ce qui (je crois) à l'époque était basé sur, sinon le même que, j-i-c.

Cette fonction fonctionne. Dans le navigateur, c'est décent mais toujours lent (Chrome). Mais lors de l'exécution de cette fonction sur un appareil tout en sélectionnant par exemple une image de 8 Mo, l'application plantera fondamentalement.

Existe-t-il un moyen d'accélérer cette compression / redimensionnement de l'image?


Informations supplémentaires

J'obtiens le fichier lui-même en utilisant cordova-plugin-camera qui est un direct lien vers le fichier sur l'appareil de l'utilisateur. Ce n'est donc pas une image base64 (mais je peux en obtenir une facilement si nécessaire).

4
Ivar Reukers 10 août 2017 à 12:27

2 réponses

Meilleure réponse

Votre fonction redimensionner est vraiment mal nommée. Ce qu'il fait est de changer la qualité de l'algorithme avec perte jpeg. Cela ne redimensionne pas vraiment vos images, et je suppose que votre image de 8 Mo est assez grande et si elle provient de l'appareil photo de l'appareil, la compression peut déjà être assez décente.

Ainsi, lorsque vous le dessinez sur le canevas, vous produisez en fait une nouvelle image brute (sans aucune compression), avec la même quantité de pixels que dans votre fichier d'origine.

Cela signifie que votre toile elle-même sera bien plus grande que les 8 Mo d'origine en mémoire. Lors de l'appel à toDataURL, il devra extraire ces données, les traiter, etc., consommant encore plus de mémoire.

Et il n'est même pas sûr qu'à la fin vous obtiendrez un fichier plus léger ...

Si vous êtes prêt à vraiment redimensionner (c'est-à-dire modifier les dimensions de) vos images, il sera plus facile pour votre appareil de les gérer:

function resize(sourceImgObj) {
    const mimeType = "image/jpeg"; // would probably be better to keep the one of the file...
    let quality = .92; // default;
    const cvs = document.createElement('canvas');
    const MAX_SIZE = 500; // in px for both height and width
    const w = sourceImgObj.naturalWidth;
    const h = sourceImgObj.naturalHeight;
    let ratio = MAX_SIZE / Math.max(w, h);
    if(ratio > 1){ // if it's smaller than our defined MAX_SIZE
      ratio = 1;
      quality = .5; // lower the jpeg quality
      }
    cvs.width = ratio * w;
    cvs.height = ratio * h;
    let ctx = cvs.getContext("2d").drawImage(sourceImgObj, 0, 0, cvs.width, cvs.height);

    // note : if it's not a problem to convert it to async then toBlob would be a better choice
    let newImageData = cvs.toDataURL(mimeType, quality);
    return newImageData;
}

inp.onchange = e => {
  const MAX_SIZE = 100000; // 100KB
  let img = new Image();
  img.src = URL.createObjectURL(inp.files[0]);
  img.onload = e => {
    // if it's not too big, return the image's current src
    let dataURL = (inp.files[0].size > MAX_SIZE) ?
    resize(img) : img.src;
    let out = new Image();
    out.src = dataURL;
    document.body.appendChild(out);
    };
  }
<input type="file" id="inp">

modifier (ivaro18)

Je voulais juste ajouter dans ma version modifiée de cette réponse. Je n'aime pas réduire le paramètre de qualité à 50%, alors je suis resté avec le rapport et l'ai modifié pour répondre à mes besoins. Cela redimensionne une image de 8,1 Mo dans un délai de 600 ms à 900 Ko.

resize(sourceImgObj, callback) {
   const mimeType = "image/jpeg";
   let quality = .92; // default;
   const cvs = document.createElement('canvas');
   const MAX_SIZE = 1024; // in px for both height and width
   const w = sourceImgObj.naturalWidth;
   const h = sourceImgObj.naturalHeight;
   let ratio = MAX_SIZE / Math.max(w, h);
   if(ratio > 1) {
       let percentage = (w >= h) ? ((w - MAX_SIZE) / w * 100) : ((h - MAX_SIZE) / h * 100);
       cvs.width = (percentage + 100)/100 * w;
       cvs.height = (percentage + 100)/100 * h;

   } else {
       cvs.width = w * ratio;
       cvs.height = h * ratio;
   }

   let ctx = cvs.getContext("2d").drawImage(sourceImgObj, 0, 0, cvs.width, cvs.height);

   let newImageData = cvs.toDataURL(mimeType, quality);
   callback(newImageData);
}
2
Ivar Reukers 18 août 2017 à 11:31

Si le seul but de la compression est de réduire la taille du fichier, vous pouvez spécifier la qualité de l'image dans la configuration du plugin, au moment de prendre une photo.

Vous pouvez essayer plusieurs valeurs différentes et voir celle qui vous convient le mieux.

https://github.com/apache/cordova-plugin-camera#cameracameraoptions--object

0
Abhishek Jain 16 août 2017 à 07:20