HTML5 - 调整画布大小时图像质量差

9
我正在制作一个图像上传页面,使用客户端图像调整(使用HTML5画布)。一切都正常工作,但问题在于图像质量不是很好。
这里是一个(正在进行中的)照片库链接,其中包含我的调整/上传代码生成的图像。这里是链接 你能看到我所说的低质量吗?许多锯齿边缘,特别是在缩略图上。有什么想法可以解决这个问题吗?
这是我生成缩略图的JavaScript代码:
img.onload = function()
{
   var canvasWidth = 150;
   var canvasHeight = 150;
   var Rz = resizeCanvasSmall(img,canvasID,canvasWidth,canvasHeight);

   ctx.drawImage(img,Rz[0],Rz[1],Rz[2],Rz[3],Rz[4],Rz[5],Rz[6],Rz[7]);
   dataurl = canvas.toDataURL("image/jpeg",0.8);  // File type and quality (0.0->1.0)
   UploadFile();
}


// Function to resize canvas (for thumbnail images)
// img = image object, canvas = canvas element ID
function resizeCanvasSmall(img,canvas,width,height)
{
var sx; //The x coordinate where to start clipping
var sy; //The y coordinate where to start clipping
var swidth; //The width of the clipped image
var sheight; //The height of the clipped image

var aspectRatio = width / height;

if (img.width > img.height) // If landscape
{
    sheight = img.height;
    swidth = img.height * aspectRatio;
    sy = 0;
    sx = (img.width - swidth) / 2;
}
else //If portrait
{
    swidth = img.width;
    sheight = img.width / aspectRatio;
    sx = 0;
    sy = (img.height - sheight) / 2;
}

document.getElementById(canvas).width = width;
document.getElementById(canvas).height = height;

return [sx,sy,swidth,sheight,0,0,width,height];
}

可能是在HTML5画布中调整图像大小的重复问题。 - Cerbrus
我在这些图片中注意到的第一件事是,调整大小不正确。我的意思是原始图像(W1,H1)缩略图像(W2,H2)。如果(W1 / W2 == H1 / H2),您将获得更好的结果。 - Joddy
1
标准并未指定drawImage使用的缩放算法,因此浏览器可以使用任何他们想要的算法。当您需要高级图像过滤(如双三次插值、sinc或lanczos)时,您将不得不自己实现它们。 - Philipp
你说的可能是对的,@Joddy。如果原始图像是3241x2321像素(例如),那么当你缩小图像时,无法完美地保持纵横比。我将尝试编写一些代码,首先剪切图像以使其可以缩小...如果成功了,我会告诉你的。 - user1912878
Jcrop是另一个用于图像上传的不错的jQuery插件。http://deepliquid.com/content/Jcrop.html - Jay S.
3个回答

6
将大量调整大小的一般规则是按二次幂跳跃,以减少别名伪影,然后在最后一步进行最终调整大小。例如:如果您需要将1000像素缩放到68像素,则将其缩小到500像素,然后是250像素,125像素,最后缩小到68像素。直到最后一次都是二次幂。
此外,您应该保留纵横比,否则对角线上会出现奇怪的别名伪影。如果您需要从非正方形源图像获取正方形缩略图,则应尽可能接近目标正方形的大小进行缩小,然后在中心裁剪(或缩小并填充)。无论如何,必须保持纵横比。
要获得比我描述的更好的结果,您必须实现自己的调整大小算法,这将比较慢(因为它们将在JS中而不是优化的本机代码中)。进入WebGL也可能是一个选择。

它仍然会是最近邻居,并且会发生颜色损失,只是看起来可能会更少些。 - dansch

3

http://output.jsbin.com/palota/1/

我制作了一个画布大小调整插件。其中一个文件可以包含在前端中。它逐像素进行处理。它不会获取附近的像素,而是对颜色进行平均,因此平均像素颜色从原始图像到新图像保持一致。

https://github.com/danschumann/limby-resize

实际要包含的文件是

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

这将产生与Photoshop或ImageMagick相同类型的调整大小。

它稍微慢一些,因为我们必须查看所有像素并重新分配每个颜色位,但对于不经常完成的工作,它可以使用。

算法背后的数学

假设我们有3个像素被调整大小为2个像素。

通常,每个像素都有4个数字:红色、绿色、蓝色、alpha。让我们只看一个简化版本,其中像素只是1个数字。

假设原始图像为:0 | 100 | 255

常规的canvas drawImage调整大小将导致0 | 100或0 | 255

有时这很好,但会失去细节,并且可能是非常丑陋和锯齿状的图像。如果你考虑一下,原始颜色的总和为355(0 + 100 + 255),每个平均像素为118.33。调整大小后的平均像素将为50或127.5,这可能看起来还好或非常不同!

在limby-resize中实现的图像算法将生成类似于ImageMagick的图像,保留所有像素数据,因此平均像素将保持不变。

我们的算法将生成以下图像:33 | 201.3
(0 * .66 + 100 * .33)|(100 * .33 + 255 * .66)
我们的总数是234.3,平均值为117.15,如果我们不将其舍入到2个小数位,它将等于第一个图像。
浏览器支持
canvas_resize.js应能够在前端包含,以便更好地在客户端进行调整大小。
var img, canvas, resized;
img = new Image;
img.onload = function(){
  canvas = document.createElement('canvas');
  canvas.width = img.width;
  canvas.height = img.height;
  canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
  resized = document.createElement('canvas');
  resized.width = 300;
  resized.height = 500;
  // see lib/canvas_resize for window.canvasResize = function(){...}
  canvasResize(canvas, resized);
  // resized will now be a properly resized version of canvas
}

img.src = '/path/to/img.jpg';

@ThomasLeu,我知道它很慢。它只使用处理器——单线程的。我想我可以编写一个WebGL过滤器并行完成相同的工作……可能会快几千倍…… - dansch

0

我不确定底层使用了什么技术,但我一直认为Processing.js 有一个相当不错的图像调整大小。文档中有一些示例用法。(更新:查看调整大小的代码,它似乎执行与您正在执行的类似过程。只是它将数据从一个图像移动到另一个图像。)

调整大小时要注意另一件事情是尽可能将图像大小减半,这样你的像素会更好地对齐。理想的调整大小示例包括50%,25%等。


谢谢推荐Processing.js。我会去看一下。我已经尝试了我所能想到的所有方法,只是使用HTML5和画布元素,但无论我尝试什么方法都没有看到任何区别。我猜这个规范可能还没有解决图像调整大小的问题?我希望我是错的,因为那将是一个遗憾。 - user1912878

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接