简体   繁体   English

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

[英]HTML5 - Poor image quality when resizing canvas

I am in the process of making and image uploading page with client side image resizing (using HTML5 canvas). 我正在使用客户端图像大小调整(使用HTML5画布)制作和上传图像的页面。 Everything is working, but the problem is that the image quality is not very good. 一切正常,但问题是图像质量不是很好。

Here is a link to a (work in progress) photo gallery with images that my resize/upload code has generated. 这是一个指向 (正在进行的工作)图片库的链接 ,其中包含我调整大小/上传代码所生成的图像。

Can you see what I mean by poor quality? 您能明白我所说的质量差吗? Lots of jagged edges, especially on the thumbnail images. 许多锯齿状的边缘,尤其是在缩略图上。 Any ideas of how to fix this? 有关如何解决此问题的任何想法?

Here is my javascript for generating the thumbnails: 这是我的用于生成缩略图的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];
}

The general rule for resizing large amounts is to go in jumps of powers of two so you have fewer aliasing artifacts, then just do one final resize for the rest of the way. 调整大量大小的一般规则是跳入2的幂次幂,这样就减少了混淆失真,然后在其余方法中仅需进行一次最终调整即可。 Ex: if you need to rescale 1000px down to 68px scale it down to 500px, then 250px, then 125px, then to 68px. 例如:如果您需要将1000px缩小到68px,则将其缩小到500px,然后是250px,然后是125px,再到68px。 It's always a power of two until the last one. 直到最后一个总是2的幂。

Also, you should preserve the aspect ratio or you'll get funky aliasing at diagonals. 另外,您应该保留长宽比,否则对角线会出现时髦的混叠现象。 If you need square thumbnails from non-square source images then size down as close as you can get to your target square while still being bigger, then crop it in the center. 如果需要非正方形源图像的正方形缩略图,则将其缩小到尽可能接近目标正方形的程度,同时还要使其更大,然后将其裁剪在中间。 (Or go smaller and pad it). (或变小并垫上它)。 You must preserve the aspect ratio at all costs. 您必须不惜一切代价保持宽高比。

To get even better results than what I've described you'd have to implement your own resizing algorithms which will be far slower (since they would be in JS instead of optimized native code). 要获得比我所描述的还要好的结果,您必须实现自己的调整大小算法,该算法的速度要慢得多(因为它们将使用JS而不是优化的本机代码)。 Jumping into WebGL might also be an option. 跳入WebGL也可能是一种选择。

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

I made a canvas resize plugin. 我做了一个画布调整大小插件。 One of the files can be included on the frontend. 其中一个文件可以包含在前端。 It goes pixel by pixel. 它逐像素地移动。 Instead of grabbing a nearby pixel, it averages the colors, so the average pixel color is the same from the original to the new image. 它没有获取附近的像素,而是对颜色进行平均,因此从原始图像到新图像的平均像素颜色相同。

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

and the actual file to include is 而要包含的实际文件是

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

This will produce the same type of resize as photoshop or imagemagick. 这将产生与photoshop或imagemagick相同类型的调整大小。

It's a little slower, because we have to look through all the pixels and redistribute every bit of color, but for jobs that aren't done often, it works. 这有点慢,因为我们必须查看所有像素并重新分配每一个颜色,但是对于不经常执行的工作,它可以工作。

Math behind the algorithm 算法背后的数学

lets say we have 3 pixels being resized into 2 pixels. 假设我们将3个像素调整为2个像素。

normally, each pixel will have 4 numbers: red, green, blue, alpha. 通常,每个像素都有4个数字:红色,绿色,蓝色,alpha。 Lets just look at a simplified version where pixels are just 1 number. 让我们看一下像素仅为1个数字的简化版本。

Lets say the original image is: 0 | 可以说原始图像是:0 | 100 | 100 | 255 255

The regular canvas drawImage resize will result in either 0 | 常规的canvas drawImage调整大小将导致0 | 0。 100 or 0 | 100或0 | 255 255

This sometimes is fine, but it loses details and it can be a very ugly and jagged image. 有时这很好,但是会丢失细节,并且可能是一个非常丑陋且参差不齐的图像。 If you think about it, the sum of all the color in the original is 355 (0 + 100 + 255), leaving the average pixel 118.33. 如果考虑一下,原件中所有颜色的总和为355(0 + 100 + 255),剩下的平均像素为118.33。 The resized average pixel would be 50 or 127.5, which could look okay or very different! 调整大小后的平均像素将为50或127.5,这看起来还可以或非常不同!

The image algorithm implemented in limby-resize will produce a similar image to imagemagick, keeping all the pixel data, so the average pixel will be the same. 以边缘调整大小实现的图像算法将产生与imagemagick相似的图像,并保留所有像素数据,因此平均像素将相同。

Our algorithm would produce the following image: 33 | 我们的算法将产生以下图像:33 | 201.3 201.3

(0 * .66 + 100 * .33) | (0 * .66 + 100 * .33)| (100 * .33 + 255 * .66) (100 * .33 + 255 * .66)

The total in ours is 234.3, leaving the average of 117.15, which is going to equal the first image ( if we weren't rounding to 2 decimals for this example ). 我们的总数为234.3,平均值为117.15,等于第一张图片(如果在此示例中我们未舍入到小数点后两位)。

Browser support 浏览器支持

canvas_resize.js should be able to be included on the frontend for better resizing client side. 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';

I'm not sure what is used under the covers, but I have always thought that Processing.js has a pretty decent looking image resize . 我不确定幕后使用的是什么,但是我一直认为Processing.js具有相当不错的图像调整大小 The documentation has some example usages. 该文档提供了一些用法示例。 ( Update : looking at the code for resize, it looks like it does a similiar process to what you are doing. Except that it takes the data from one image and moves it to another.) 更新 :查看要调整大小的代码,它看起来像对您正在做的事情类似的过程。除了它从一个图像中获取数据并将其移至另一个图像。)

Another thing to note when resizing is to always half the image size if possible, so that your pixels line up better. 调整大小时要注意的另一件事是,如果可能,请始终将图像大小始终减半,以使像素更好地对齐。 Examples of ideal resizes would be 50%, 25%, etc. 理想调整大小的示例是50%,25%等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM