J'utilise des éléments de canevas html5 pour redimensionner les images dans mon navigateur. Il s'avère que la qualité est très faible. J'ai trouvé ceci: Désactivez l'interpolation lors de la mise à l'échelle d'un <canvas>, mais cela n'aide pas à augmenter la qualité.
Vous trouverez ci-dessous mon code css et js ainsi que l'image scallée avec Photoshop et mise à l'échelle dans l'API canvas.
Que dois-je faire pour obtenir une qualité optimale lors de la mise à l'échelle d'une image dans le navigateur?
Remarque: je souhaite réduire une grande image à une petite image, modifier la couleur d'une toile et envoyer le résultat de la toile au serveur.
CSS:
canvas, img {
image-rendering: optimizeQuality;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
JS:
var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {
var originalContext = $originalCanvas[0].getContext('2d');
originalContext.imageSmoothingEnabled = false;
originalContext.webkitImageSmoothingEnabled = false;
originalContext.mozImageSmoothingEnabled = false;
originalContext.drawImage(this, 0, 0, 379, 500);
});
L'image redimensionnée avec Photoshop:
L'image redimensionnée sur toile:
Éditer:
J'ai essayé de faire une réduction d'échelle en plus d'une étape comme proposé dans:
Redimensionner une image dans un canevas HTML5 et un canevas Html5 drawImage: comment appliquer l'anticrénelage
C'est la fonction que j'ai utilisée:
function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 2;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
console.log("Step: "+i);
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// copy back to canvas
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
}
Voici le résultat si j'utilise un dimensionnement en 2 étapes:
Voici le résultat si j'utilise un dimensionnement en 3 étapes:
Voici le résultat si j'utilise un dimensionnement en 4 étapes:
Voici le résultat si j'utilise un dimensionnement en 20 étapes:
Remarque: Il s'avère que d'une étape à deux étapes, il y a une grande amélioration de la qualité de l'image, mais plus vous ajoutez d'étapes au processus, plus l'image devient floue.
Y a-t-il un moyen de résoudre le problème que l'image devient plus floue à mesure que vous ajoutez d'étapes?
Edit 2013-10-04: J'ai essayé l'algorithme de GameAlchemist. Voici le résultat par rapport à Photoshop.
Image de PhotoShop:
Algorithme de GameAlchemist:
la source
Réponses:
Puisque votre problème est de réduire l'échelle de votre image, il ne sert à rien de parler d'interpolation - qui consiste à créer des pixels -. Le problème ici est le sous-échantillonnage.
Pour sous-échantillonner une image, nous devons transformer chaque carré de p * p pixels de l'image d'origine en un seul pixel de l'image de destination.
Pour des raisons de performances, les navigateurs effectuent un sous-échantillonnage très simple: pour créer la plus petite image, ils choisiront juste UN pixel dans la source et utiliseront sa valeur pour la destination. qui «oublie» certains détails et ajoute du bruit.
Pourtant, il y a une exception à cela: comme le sous-échantillonnage d'image 2X est très simple à calculer (en moyenne 4 pixels pour en faire un) et est utilisé pour les pixels rétine / HiDPI, ce cas est géré correctement - le navigateur utilise 4 pixels pour faire une-.
MAIS ... si vous utilisez plusieurs fois un sous-échantillonnage 2X, vous serez confronté au problème que les erreurs d'arrondi successives ajouteront trop de bruit.
Pire encore, vous ne redimensionnerez pas toujours par une puissance de deux, et redimensionner à la puissance la plus proche + un dernier redimensionnement est très bruyant.
Ce que vous recherchez, c'est un sous-échantillonnage au pixel parfait, c'est-à-dire: un ré-échantillonnage de l'image qui prendra en compte tous les pixels d'entrée - quelle que soit l'échelle -.
Pour ce faire, nous devons calculer, pour chaque pixel d'entrée, sa contribution à un, deux ou quatre pixels de destination selon que la projection mise à l'échelle des pixels d'entrée se trouve juste à l'intérieur d'un pixel de destination, chevauche une bordure X, une bordure Y ou les deux. .
(Un schéma serait bien ici, mais je n'en ai pas.)
Voici un exemple de l'échelle de la toile par rapport à mon échelle parfaite en pixels à l'échelle 1/3 d'un zombat.
Notez que l'image peut être mise à l'échelle dans votre navigateur et qu'elle est .jpegized par SO.
Pourtant, on voit qu'il y a beaucoup moins de bruit surtout dans l'herbe derrière le wombat, et les branches à sa droite. Le bruit dans la fourrure la rend plus contrastée, mais on dirait qu'il a des cheveux blancs - contrairement à l'image source -.
La bonne image est moins accrocheuse mais définitivement plus jolie.
Voici le code pour effectuer la réduction d'échelle parfaite des pixels:
résultat du violon: http://jsfiddle.net/gamealchemist/r6aVp/embedded/result/
violon lui-même: http://jsfiddle.net/gamealchemist/r6aVp/
C'est assez gourmand en mémoire, puisqu'une mémoire tampon flottante est nécessaire pour stocker les valeurs intermédiaires de l'image de destination (-> si on compte le canevas résultat, on utilise 6 fois la mémoire de l'image source dans cet algorithme).
C'est aussi assez cher, puisque chaque pixel source est utilisé quelle que soit la taille de la destination, et nous devons payer pour le getImageData / putImageDate, assez lent également.
Mais il n'y a aucun moyen d'être plus rapide que de traiter chaque valeur source dans ce cas, et la situation n'est pas si mauvaise: pour mon image 740 * 556 d'un wombat, le traitement prend entre 30 et 40 ms.
la source
Rééchantillonnage rapide de la toile avec une bonne qualité: http://jsfiddle.net/9g9Nv/442/
Mise à jour: version 2.0 (plus rapide, web workers + objets transférables) - https://github.com/viliusle/Hermite-resize
la source
Suggestion 1 - Prolonger la canalisation du processus
Vous pouvez utiliser step-down comme je le décris dans les liens auxquels vous faites référence, mais vous semblez les utiliser de manière incorrecte.
Il n'est pas nécessaire de redimensionner les images à des ratios supérieurs à 1: 2 (généralement, mais sans s'y limiter). C'est là que vous devez effectuer une réduction drastique de l'échelle, vous devez le diviser en deux (et rarement, plus) étapes en fonction du contenu de l'image (en particulier lorsque des hautes fréquences telles que des lignes fines se produisent).
Chaque fois que vous sous-échantillonnez une image, vous perdez des détails et des informations. Vous ne pouvez pas vous attendre à ce que l'image résultante soit aussi claire que l'original.
Si vous réduisez ensuite les images en plusieurs étapes, vous perdrez beaucoup d'informations au total et le résultat sera médiocre comme vous l'avez déjà remarqué.
Essayez avec une seule étape supplémentaire, ou au sommet de deux.
Convolutions
Dans le cas de Photoshop, notez qu'il applique une convolution après que l'image a été rééchantillonnée, telle que la netteté. Ce n'est pas seulement une interpolation bi-cubique qui a lieu, donc pour émuler pleinement Photoshop, nous devons également ajouter les étapes que Photoshop effectue (avec la configuration par défaut).
Pour cet exemple, j'utiliserai ma réponse originale à laquelle vous vous référez dans votre article, mais j'y ai ajouté une convolution affinée pour améliorer la qualité en tant que post-processus (voir la démo en bas).
Voici le code pour ajouter un filtre de netteté (il est basé sur un filtre de convolution générique - j'ai mis la matrice de poids pour l'accentuation à l'intérieur ainsi qu'un facteur de mixage pour ajuster la prononciation de l'effet):
Usage:
La
mixFactor
valeur est comprise entre [0.0, 1.0] et vous permet de minimiser l'effet de netteté - règle empirique: moins il y a de taille, moins l'effet est nécessaire.Fonction (basée sur cet extrait ):
Le résultat de l'utilisation de cette combinaison sera:
DÉMO EN LIGNE ICI
Selon la quantité de netteté que vous souhaitez ajouter au mélange, vous pouvez obtenir un résultat de "flou" par défaut à très net:
Suggestion 2 - Implémentation d'algorithme de bas niveau
Si vous voulez obtenir le meilleur résultat en termes de qualité, vous devrez aller de bas niveau et envisager d'implémenter par exemple ce tout nouvel algorithme pour ce faire.
Voir Sous-échantillonnage d'image dépendant de l'interpolation (2011) de l'IEEE.
Voici un lien vers l'article dans son intégralité (PDF) .
Il n'y a pas d'implémentation de cet algorithme dans JavaScript AFAIK ou pour le moment, vous êtes donc prêt à vous lancer dans cette tâche.
L'essence est (extraits de l'article):
Abstrait
(voir le lien fourni pour tous les détails, formules, etc.)
la source
Si vous souhaitez utiliser uniquement le canevas, le meilleur résultat sera avec plusieurs descentes. Mais ce n'est pas encore assez bon. Pour une meilleure qualité, vous avez besoin d'une implémentation pure js. Nous venons de sortir pica - un downscaler à grande vitesse avec une qualité / vitesse variable. En bref, il redimensionne 1280 * 1024px en ~ 0,1s, et l'image 5000 * 3000px en 1s, avec la plus haute qualité (filtre lanczos à 3 lobes). Pica a une démo , où vous pouvez jouer avec vos images, vos niveaux de qualité et même l'essayer sur des appareils mobiles.
Pica n'a pas encore de masque flou, mais cela sera ajouté très bientôt. C'est beaucoup plus facile que d'implémenter un filtre de convolution haute vitesse pour le redimensionnement.
la source
Pourquoi utiliser le canevas pour redimensionner les images? Les navigateurs modernes utilisent tous une interpolation bicubique - le même processus utilisé par Photoshop (si vous le faites correctement) - et ils le font plus rapidement que le processus de canevas. Spécifiez simplement la taille d'image souhaitée (utilisez une seule dimension, hauteur ou largeur, pour redimensionner proportionnellement).
Ceci est pris en charge par la plupart des navigateurs, y compris les versions ultérieures d'IE. Les versions antérieures peuvent nécessiter un CSS spécifique au navigateur .
Une fonction simple (en utilisant jQuery) pour redimensionner une image serait comme ceci:
Ensuite, utilisez simplement la valeur renvoyée pour redimensionner l'image dans une ou les deux dimensions.
De toute évidence, vous pouvez apporter différentes améliorations, mais cela fait le travail.
Collez le code suivant dans la console de cette page et regardez ce qui arrive aux gravatars:
la source
Ce n'est pas la bonne réponse pour les personnes qui ont vraiment besoin de redimensionner l'image elle-même, mais simplement de réduire la taille du fichier .
J'ai eu un problème avec les images «directement depuis l'appareil photo», que mes clients téléchargeaient souvent au format JPEG «non compressé».
Ce n'est pas si connu que le canevas prend en charge (dans la plupart des navigateurs 2017) pour changer la qualité du JPEG
Avec cette astuce, je pourrais réduire 4k x 3k photos avec> 10 Mo à 1 ou 2 Mo, bien sûr que cela dépend de vos besoins.
regarde ici
la source
Voici un service angulaire réutilisable pour le redimensionnement d'image / toile de haute qualité: https://gist.github.com/fisch0920/37bac5e741eaec60e983
Le service prend en charge la convolution lanczos et la réduction d'échelle par étapes. L'approche par convolution est de meilleure qualité au prix d'être plus lente, tandis que l'approche de réduction d'échelle par étapes produit des résultats raisonnablement anticrénelés et est nettement plus rapide.
Exemple d'utilisation:
la source
Il s'agit du filtre de redimensionnement Hermite amélioré qui utilise 1 travailleur pour que la fenêtre ne se fige pas.
https://github.com/calvintwr/blitz-hermite-resize
la source
J'ai trouvé une solution qui n'a pas besoin d'accéder directement aux données de pixels et de les parcourir pour effectuer le sous-échantillonnage. Selon la taille de l'image, cela peut être très gourmand en ressources et il serait préférable d'utiliser les algorithmes internes du navigateur.
La fonction drawImage () utilise une méthode de rééchantillonnage du plus proche voisin par interpolation linéaire. Cela fonctionne bien lorsque vous ne redimensionnez pas plus de la moitié de la taille d'origine .
Si vous effectuez une boucle pour ne redimensionner que la moitié au maximum à la fois, les résultats seraient assez bons et beaucoup plus rapides que l'accès aux données de pixels.
Cette fonction sous-échantillonne de moitié à la fois jusqu'à atteindre la taille souhaitée:
la source
Peut-être que vous pouvez essayer ceci, que j'utilise toujours dans mon projet. De cette façon, vous pouvez non seulement obtenir une image de haute qualité, mais tout autre élément sur votre toile.
la source
au lieu de .85 , si nous ajoutons 1.0 . Vous obtiendrez une réponse exacte.
Vous pouvez obtenir une image claire et lumineuse. Vérifiez s'il vous plaît
la source
J'essaie vraiment d'éviter de parcourir les données d'image, en particulier sur des images plus grandes. Ainsi, j'ai trouvé un moyen assez simple de réduire décemment la taille de l'image sans aucune restriction ni limitation en utilisant quelques étapes supplémentaires. Cette routine descend au demi-pas le plus bas possible avant la taille cible souhaitée. Ensuite, il le met à l'échelle jusqu'à deux fois la taille cible, puis à nouveau de moitié. Cela semble drôle au début, mais les résultats sont incroyablement bons et y parviennent rapidement.
la source
context.scale(xScale, yScale)
la source
DEMO : Redimensionner des images avec JS et HTML Canvas Demo Fiddler.
Vous pouvez trouver 3 méthodes différentes pour effectuer ce redimensionnement, qui vous aideront à comprendre comment le code fonctionne et pourquoi.
https://jsfiddle.net/1b68eLdr/93089/
Le code complet de la démo et de la méthode TypeScript que vous pouvez utiliser dans votre code se trouve dans le projet GitHub.
https://github.com/eyalc4/ts-image-resizer
Voici le code final:
la source