Veuillez regarder l'exemple suivant:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img = new Image();
img.onload = function(){
canvas.width = 400;
canvas.height = 150;
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 400, 150);
}
img.src = "http://openwalls.com/image/1734/colored_lines_on_blue_background_1920x1200.jpg";
Comme vous le voyez, l'image n'est pas anti-aliasée bien qu'il soit dit que drawImage applique l'anti-aliasing automatiquement. J'ai essayé beaucoup de façons différentes mais cela ne semble pas fonctionner. Pourriez-vous s'il vous plaît me dire comment je peux obtenir une image anti-aliasée? Merci.
Certaines images sont très difficiles à sous-échantillonner et interpolez comme celle-ci avec des courbes lorsque vous souhaitez passer d'une taille grande à une taille petite.
Les navigateurs semblent généralement utiliser une interpolation bi-linéaire (échantillonnage 2x2) avec l'élément canvas plutôt que bi-cubique (échantillonnage 4x4) pour des raisons de performances (probables).
Si le pas est trop grand, il n’ya tout simplement pas assez de pixels à échantillonner, ce qui se reflète dans le résultat.
Du point de vue du signal/DSP, cela peut être perçu comme une valeur de seuil trop élevée pour un filtre passe-bas, ce qui peut entraîner un repliement du spectre si le signal contient de nombreuses hautes fréquences (détails).
Mise à jour 2018:
Voici une astuce intéressante que vous pouvez utiliser pour les navigateurs et qui supporte la propriété filter
sur le contexte 2D. Cela produit une image floue, qui est essentiellement identique à un rééchantillonnage, puis réduit. Cela permet de grandes marches, mais ne nécessite que deux marches et deux tirages.
Pré-flou en utilisant le nombre d'étapes (taille d'origine/taille de destination/2) comme rayon (vous devrez peut-être ajuster ce paramètre de manière heuristique en fonction du navigateur et des étapes impaires/paires - ici, uniquement illustré de manière simplifiée):
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (typeof ctx.filter === "undefined") {
alert("Sorry, the browser doesn't support Context2D filters.")
}
const img = new Image;
img.onload = function() {
// step 1
const oc = document.createElement('canvas');
const octx = oc.getContext('2d');
oc.width = this.width;
oc.height = this.height;
// steo 2: pre-filter image using steps as radius
const steps = (oc.width / canvas.width)>>1;
octx.filter = `blur(${steps}px)`;
octx.drawImage(this, 0, 0);
// step 3, draw scaled
ctx.drawImage(oc, 0, 0, oc.width, oc.height, 0, 0, canvas.width, canvas.height);
}
img.src = "//i.stack.imgur.com/cYfuM.jpg";
body{ background-color: ivory; }
canvas{border:1px solid red;}
<br/><p>Original was 1600x1200, reduced to 400x300 canvas</p><br/>
<canvas id="canvas" width=400 height=250></canvas>
Prise en charge du filtre en ogf oct/2018:
CanvasRenderingContext2D.filter
api.CanvasRenderingContext2D.filter
On Standard Track, Experimental
https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/filter
DESKTOP > |Chrome |Edge |Firefox |IE |Opera |Safari
:----------------|:--------:|:--------:|:--------:|:--------:|:--------:|:--------
filter ! | 52 | ? | 49 | - | - | -
MOBILE > |Chrome/A |Edge/mob |Firefox/A |Opera/A |Safari/iOS|Webview/A
:----------------|:--------:|:--------:|:--------:|:--------:|:--------:|:--------
filter ! | 52 | ? | 49 | - | - | 52
! = Experimental
Data from MDN - "npm i -g mdncomp" (c) epistemex
Mise à jour 2017: Il y a maintenant un nouvelle propriété défini dans les spécifications pour définir la qualité du rééchantillonnage:
context.imageSmoothingQuality = "low|medium|high"
Il n'est actuellement pris en charge que par Chrome. Il appartient au fournisseur de décider des méthodes réellement utilisées par niveau, mais il est raisonnable de supposer que Lanczos est "élevé" ou de qualité équivalente. Cela signifie que vous pouvez ignorer complètement les étapes, ou que des étapes plus grandes peuvent être utilisées avec moins de redessin, en fonction de la taille de l'image et
Prise en charge de imageSmoothingQuality
:
CanvasRenderingContext2D.imageSmoothingQuality
api.CanvasRenderingContext2D.imageSmoothingQuality
On Standard Track, Experimental
https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality
DESKTOP > |Chrome |Edge |Firefox |IE |Opera |Safari
:----------------------|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:
imageSmoothingQuality !| 54 | ? | - | ? | 41 | Y
MOBILE > |Chrome/A |Edge/mob |Firefox/A |Opera/A |Safari/iOS|Webview/A
:----------------------|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:
imageSmoothingQuality !| 54 | ? | - | 41 | Y | 54
! = Experimental
Data from MDN - "npm i -g mdncomp" (c) epistemex
navigateur. Jusque là..:
Fin de la transmission
La solution consiste à utiliser étape par étape pour obtenir un résultat correct. Réduire signifie que vous réduisez la taille par étapes pour permettre à la plage d'interpolation limitée de couvrir suffisamment de pixels pour l'échantillonnage.
Cela permettra également d’obtenir de bons résultats avec l’interpolation bi-linéaire (elle se comporte en réalité assez bi-cubique) et les frais généraux sont minimes, car il y a moins de pixels à échantillonner à chaque étape.
L’idéal est d’aller à la moitié de la résolution à chaque étape jusqu’à ce que vous définissiez la taille cible (merci à Joe Mabel de l’avoir mentionné!).
Utilisation de la mise à l'échelle directe comme dans la question d'origine:
en utilisant les étapes ci-dessous:
Dans ce cas, vous devrez vous retirer en 3 étapes:
À l'étape 1, nous réduisons l'image à moitié en utilisant un canevas hors écran:
// step 1 - create off-screen canvas
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);
L'étape 2 réutilise le canevas hors écran et dessine à nouveau l'image réduite de moitié:
// step 2
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
Et on dessine encore une fois sur la toile principale, encore une fois réduite à moitié mais à la taille finale:
// step 3
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
Vous pouvez calculer le nombre total d'étapes nécessaires à l'aide de cette formule (elle comprend l'étape finale pour définir la taille cible):
steps = Math.ceil(Math.log(sourceWidth / targetWidth) / Math.log(2))
En complément de la réponse de Ken, voici une autre solution permettant d'effectuer le sous-échantillonnage par moitiés (pour que le résultat soit satisfaisant à l'aide de l'algorithme du navigateur):
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
var getBase64Image = function(img, quality) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
//----- Origin draw ---
ctx.drawImage(img, 0, 0, img.width, img.height);
//------ reduced draw ---
var canvas2 = document.createElement("canvas");
canvas2.width = img.width * quality;
canvas2.height = img.height * quality;
var ctx2 = canvas2.getContext("2d");
ctx2.drawImage(canvas, 0, 0, img.width * quality, img.height * quality);
// -- back from reduced draw ---
ctx.drawImage(canvas2, 0, 0, img.width, img.height);
var dataURL = canvas.toDataURL("image/png");
return dataURL;
// return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
J'ai créé un service réutilisable Angular permettant de redimensionner les images de haute qualité pour les personnes intéressées: https://Gist.github.com/fisch0920/37bac5e741eaec60e983)
Le service inclut l'approche de réduction progressive de Ken, ainsi qu'une version modifiée de l'approche de convolution de Lanczos trouvée ici .
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
})
})
Si quelqu'un d'autre cherche toujours une réponse, il existe un autre moyen d'utiliser une image d'arrière-plan au lieu de drawImage (). Vous ne perdrez aucune qualité d'image de cette façon.
JS:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var url = "http://openwalls.com/image/17342/colored_lines_on_blue_background_1920x1200.jpg";
img=new Image();
img.onload=function(){
canvas.style.backgroundImage = "url(\'" + url + "\')"
}
img.src="http://openwalls.com/image/17342/colored_lines_on_blue_background_1920x1200.jpg";