Dun*_*dar 77 html5 interpolation canvas antialiasing html5-canvas
请看下面的例子:
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";
Run Code Online (Sandbox Code Playgroud)
如您所见,虽然据说drawImage会自动应用抗锯齿,但图像不会消除锯齿.我尝试了很多不同的方法,但似乎没有用.你能告诉我如何获得抗锯齿图像吗?谢谢.
小智 163
当你想要从大尺寸变为小尺寸时,有些图像很难进行下采样和插值,例如带有曲线的图像.
由于(可能)性能原因,浏览器通常使用双向线性(2x2采样)插值与画布元素而不是双三次(4x4采样).
如果步长太大,那么根本没有足够的像素可以从结果中反映出来.
从信号/ DSP的角度来看,您可以将此视为低通滤波器的阈值设置得太高,如果信号中存在许多高频(细节),则可能导致混叠.
更新2018年:
这是一个巧妙的技巧,你可以用于支持filter2D上下文属性的浏览器.这预先模糊了与重新采样基本相同的图像,然后缩小.这允许大步骤但只需要两步和两次抽取.
使用步数(原始大小/目标大小/ 2)作为半径进行预模糊(您可能需要根据浏览器和奇数/偶数步骤启发式调整此处 - 此处仅显示为简化):
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";Run Code Online (Sandbox Code Playgroud)
body{ background-color: ivory; }
canvas{border:1px solid red;}Run Code Online (Sandbox Code Playgroud)
<br/><p>Original was 1600x1200, reduced to 400x300 canvas</p><br/>
<canvas id="canvas" width=400 height=250></canvas>Run Code Online (Sandbox Code Playgroud)
支持过滤器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) epistemexRun Code Online (Sandbox Code Playgroud)
2017年更新:现在,规范中定义了一个用于设置重新采样质量的新属性:
context.imageSmoothingQuality = "low|medium|high"
Run Code Online (Sandbox Code Playgroud)
它目前仅在Chrome中受支持.每个级别使用的实际方法由供应商决定,但是将Lanczos视为"高"或质量等同的是合理的.这意味着可以完全跳过降压,或者可以使用更大的步骤,重绘次数更少,具体取决于图像大小和
支持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) epistemexRun Code Online (Sandbox Code Playgroud)
浏览器.在那之前..:
传输结束
解决方案是使用降压来获得正确的结果.降压意味着您逐步减小尺寸,以允许有限的插值范围覆盖足够的像素进行采样.
这也可以通过双线性插值获得良好的结果(实际上,这样做时就像双立方体一样)并且开销很小,因为每个步骤中的采样像素较少.
理想的一步是在每一步中达到分辨率的一半,直到你设定目标尺寸(感谢Joe Mabel提到这个!).
使用原始问题中的直接缩放:

使用降压如下所示:

在这种情况下,您需要分步执行3个步骤:
在第1步中,我们使用离屏画布将图像缩小为一半:
// 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);
Run Code Online (Sandbox Code Playgroud)
步骤2重用屏幕外画布并将图像再次缩小为一半:
// step 2
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
Run Code Online (Sandbox Code Playgroud)
我们再次画到主画布上,再缩小到一半但最终尺寸:
// step 3
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
Run Code Online (Sandbox Code Playgroud)
您可以使用此公式计算所需的总步骤数(包括设置目标大小的最后一步):
steps = Math.ceil(Math.log(sourceWidth / targetWidth) / Math.log(2))
Run Code Online (Sandbox Code Playgroud)
除了 Ken 的答案之外,这里还有另一个执行一半下采样的解决方案(因此使用浏览器的算法结果看起来不错):
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] );
Run Code Online (Sandbox Code Playgroud)