调整 HTML5 画布中图像的大小
我正在尝试使用 javascript 和 canvas 元素在客户端创建缩略图,但是当我缩小图像时,它看起来很糟糕。看起来好像是在 Photoshop 中缩小了尺寸,并将重采样设置为“最近邻居”而不是双三次。我知道有可能让它看起来正确,因为这个网站可以使用画布也可以做得很好。我尝试使用与“[Source]”链接中所示相同的代码,但它看起来仍然很糟糕。我是否缺少某些东西,需要设置某些设置或其他什么?
编辑:
我正在尝试调整 jpg 的大小。我尝试在链接网站和 Photoshop 中调整相同 jpg 的大小,缩小后看起来不错。
这是相关代码:
reader.onloadend = function(e)
{
var img = new Image();
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
img.onload = function()
{
var ratio = 1;
if(img.width > maxWidth)
ratio = maxWidth / img.width;
else if(img.height > maxHeight)
ratio = maxHeight / img.height;
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
};
img.src = reader.result;
}
EDIT2:
似乎我错了,链接的网站在缩小图像尺寸方面并没有做得更好。我尝试了建议的其他方法,但没有一个看起来更好。这是不同方法的结果:
Photoshop:
画布:
带有图像渲染的图像:optimizeQuality 设置并按宽度/高度缩放:
具有图像渲染的图像:optimizeQuality 设置并使用 -moz-transform 缩放:
画布调整大小在 pixastic 上:
我想这意味着 Firefox 没有像它应该的那样使用双三次采样。我只需要等到他们真正添加它。
编辑3:
I'm trying to create a thumbnail image on the client side using javascript and a canvas element, but when I shrink the image down, it looks terrible. It looks as if it was downsized in photoshop with the resampling set to 'Nearest Neighbor' instead of Bicubic. I know its possible to get this to look right, because this site can do it just fine using a canvas as well. I've tried using the same code they do as shown in the "[Source]" link, but it still looks terrible. Is there something I'm missing, some setting that needs to be set or something?
EDIT:
I'm trying to resize a jpg. I have tried resizing the same jpg on the linked site and in photoshop, and it looks fine when downsized.
Here is the relevant code:
reader.onloadend = function(e)
{
var img = new Image();
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
img.onload = function()
{
var ratio = 1;
if(img.width > maxWidth)
ratio = maxWidth / img.width;
else if(img.height > maxHeight)
ratio = maxHeight / img.height;
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
};
img.src = reader.result;
}
EDIT2:
Seems I was mistaken, the linked website wasn't doing any better of a job of downsizing the image. I tried the other methods suggested and none of them look any better. This is what the different methods resulted in:
Photoshop:
Canvas:
Image with image-rendering: optimizeQuality set and scaled with width/height:
Image with image-rendering: optimizeQuality set and scaled with -moz-transform:
Canvas resize on pixastic:
I guess this means firefox isn't using bicubic sampling like its supposed to. I'll just have to wait until they actually add it.
EDIT3:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
那么,如果所有浏览器(实际上,Chrome 5 给了我一个相当不错的浏览器)都无法提供足够好的重采样质量,您该怎么办?然后你自己实现它们!噢,来吧,我们正在进入 Web 3.0 的新时代,兼容 HTML5 的浏览器,超级优化的 JIT javascript 编译器,多核(†)机器,拥有大量内存,你在害怕什么?嘿嘿,javascript里面有java这个词,这样应该可以保证性能吧?看哪,缩略图生成代码:
...用它你可以产生这样的结果!
所以无论如何,这是示例的“固定”版本:
现在是时候把你最好的浏览器拿出来看看哪一款最不可能让你的客户血压升高了!
嗯,我的讽刺标签在哪里?
(由于代码的许多部分基于Anrieff Gallery Generator,它是否也受 GPL2 保护?我不知道)
† 实际上由于javascript的限制,不支持多核。
So what do you do if all the browsers (actually, Chrome 5 gave me quite good one) won't give you good enough resampling quality? You implement them yourself then! Oh come on, we're entering the new age of Web 3.0, HTML5 compliant browsers, super optimized JIT javascript compilers, multi-core(†) machines, with tons of memory, what are you afraid of? Hey, there's the word java in javascript, so that should guarantee the performance, right? Behold, the thumbnail generating code:
...with which you can produce results like these!
so anyway, here is a 'fixed' version of your example:
Now it's time to pit your best browsers out there and see which one will least likely increase your client's blood pressure!
Umm, where's my sarcasm tag?
(since many parts of the code is based on Anrieff Gallery Generator is it also covered under GPL2? I don't know)
† actually due to limitation of javascript, multi-core is not supported.
使用 Hermite 过滤器和 JavaScript 进行快速图像调整大小/重新采样算法。支持透明度,提供良好的质量。预览:
更新:添加了 2.0 版GitHub(更快,网络工作者+可传输对象)。终于我成功了!
Git:https://github.com/viliusle/Hermite-resize
演示:http://viliusle.github.io/miniPaint/
Fast image resize/resample algorithm using Hermite filter with JavaScript. Support transparency, gives good quality. Preview:
Update: version 2.0 added on GitHub (faster, web workers + transferable objects). Finally i got it working!
Git: https://github.com/viliusle/Hermite-resize
Demo: http://viliusle.github.io/miniPaint/
尝试 pica - 这是一个高度优化的缩放器,具有可选的算法。请参阅演示。
例如,第一篇文章中的原始图像使用 Lanczos 滤镜和 3px 窗口在 120 毫秒内调整大小,或者使用 Box 滤镜和 0.5 像素窗口在 60 毫秒内调整大小。对于 17mb 的巨大图像(5000x3000px),调整大小在桌面上大约需要 1 秒,在移动设备上大约需要 3 秒。
所有调整大小的原则都在这个线程中得到了很好的描述,并且 Pica 没有添加火箭科学。但它针对现代 JIT-s 进行了很好的优化,并且可以开箱即用(通过 npm 或 Bower)。此外,它在可用时使用网络工作者来避免界面冻结。
我还计划很快添加锐化蒙版支持,因为它在缩小比例后非常有用。
Try pica - that's a highly optimized resizer with selectable algorythms. See demo.
For example, original image from first post is resized in 120ms with Lanczos filter and 3px window or 60ms with Box filter and 0.5px window. For huge 17mb image 5000x3000px resize takes ~1s on desktop and 3s on mobile.
All resize principles were described very well in this thread, and pica does not add rocket science. But it's optimized very well for modern JIT-s, and is ready to use out of box (via npm or bower). Also, it use webworkers when available to avoid interface freezes.
I also plan to add unsharp mask support soon, because it's very useful after downscale.
我知道这是一个旧线程,但对于像我这样的人来说,几个月后第一次遇到这个问题可能会有用。
这是一些在每次重新加载图像时调整图像大小的代码。我知道这根本不是最佳的,但我将其作为概念证明提供。
另外,很抱歉使用 jQuery 来实现简单的选择器,但我对语法感到太舒服了。
我的 createImage 函数在加载文档时被调用一次,之后每次窗口接收到调整大小事件时都会调用它。
我在 Mac 上的 Chrome 6 和 Firefox 3.6 中对其进行了测试。这种“技术”吃处理器就像吃夏天的冰淇淋一样,但它确实有效。
I know this is an old thread but it might be useful for some people such as myself that months after are hitting this issue for the first time.
Here is some code that resizes the image every time you reload the image. I am aware this is not optimal at all, but I provide it as a proof of concept.
Also, sorry for using jQuery for simple selectors but I just feel too comfortable with the syntax.
My createImage function is called once when the document is loaded and after that it is called every time the window receives a resize event.
I tested it in Chrome 6 and Firefox 3.6, both on the Mac. This "technique" eats processor as it if was ice cream in the summer, but it does the trick.
我提出了一些算法来在 html 画布像素数组上进行图像插值,这些算法可能在这里有用:
https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2
这些可以是复制/粘贴,并且可以在网络工作人员内部使用来调整图像大小(或任何其他需要插值的操作 - 我目前正在使用它们来删除图像)。
我还没有添加上面的 lanczos 内容,所以如果您愿意,请随意添加它作为比较。
I've put up some algorithms to do image interpolation on html canvas pixel arrays that might be useful here:
https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2
These can be copy/pasted and can be used inside of web workers to resize images (or any other operation that requires interpolation - I'm using them to defish images at the moment).
I haven't added the lanczos stuff above, so feel free to add that as a comparison if you'd like.
这是改编自 @Telanor 代码的 JavaScript 函数。当将图像 base64 作为第一个参数传递给函数时,它返回调整大小的图像的 base64。 maxWidth 和 maxHeight 是可选的。
This is a javascript function adapted from @Telanor's code. When passing a image base64 as first argument to the function, it returns the base64 of the resized image. maxWidth and maxHeight are optional.
我强烈建议您查看 此链接 并确保其设置为 true。
I'd highly suggest you check out this link and make sure it is set to true.
如果您只是想调整图像大小,我建议使用 CSS 设置图像的
宽度
和高度
。下面是一个简单的示例:请注意,
height
和width
也可以使用 JavaScript 设置。以下是快速代码示例:此外,为了确保调整大小后的图像看起来不错,请将以下 css 规则添加到图像选择器:
-ms-interpolation-mode: bicubic
:在IE7中引入image-rendering: optimizationQuality
:在 FireFox 3.6 中引入据我所知,除了 IE 之外的所有浏览器都使用默认情况下,双三次算法会调整图像大小,因此调整后的图像在 Firefox 和 Chrome 中应该看起来不错。
如果设置 css
width
和height
不起作用,您可能需要使用 csstransform
:-moz-transform:scale(sx[, sy])
-webkit-transform:scale( sx[, sy])
如果出于某种原因您需要使用画布,请注意有两种方法可以调整图像大小:通过使用css 或以较小尺寸绘制图像。
有关更多详细信息,请参阅此问题。
If you're simply trying to resize an image, I'd recommend setting
width
andheight
of the image with CSS. Here's a quick example:Note that the
height
andwidth
can also be set using JavaScript. Here's quick code sample:Also, to ensure that the resized image looks good, add the following css rules to image selector:
-ms-interpolation-mode: bicubic
: introduce in IE7image-rendering: optimizeQuality
: introduced in FireFox 3.6As far as I can tell, all browsers except IE using an bicubic algorithm to resize images by default, so your resized images should look good in Firefox and Chrome.
If setting the css
width
andheight
doesn't work, you may want to play with a csstransform
:-moz-transform: scale(sx[, sy])
-webkit-transform:scale(sx[, sy])
If for whatever reason you need to use a canvas, please note that there are two ways an image can be resize: by resizing the canvas with css or by drawing the image at a smaller size.
See this question for more details.
我通过右键单击 Firefox 中的画布元素并另存为来获得此图像。
所以无论如何,这是示例的“固定”版本:
i got this image by right clicking the canvas element in firefox and saving as.
so anyway, here is a 'fixed' version of your example:
为了调整图像的宽度小于原始宽度,我使用:
并且它有效=)。
For resizing to image with width less that original, i use:
and it works =).
我有一种感觉,我编写的模块会产生与 Photoshop 类似的结果,因为它通过平均保留颜色数据,而不是应用算法。它有点慢,但对我来说它是最好的,因为它保留了所有颜色数据。
https://github.com/danschumann/limby-resize/ blob/master/lib/canvas_resize.js
它不会采用最近的邻居并丢弃其他像素,或者对一组进行采样并取随机平均值。它采用每个源像素应输出到目标像素的确切比例。源中的平均像素颜色将是目标中的平均像素颜色,我认为这些其他公式不会。
如何使用的示例位于底部
https://github.com/danschumann/limby-resize
2018 年 10 月更新< /strong>:现在我的例子比其他任何东西都更具学术性。 Webgl 几乎是 100%,因此您最好调整大小以产生类似的结果,但速度更快。我相信 PICA.js 就是这么做的。 –
I have a feeling the module I wrote will produce similar results to photoshop, as it preserves color data by averaging them, not applying an algorithm. It's kind of slow, but to me it is the best, because it preserves all the color data.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
It doesn't take the nearest neighbor and drop other pixels, or sample a group and take a random average. It takes the exact proportion each source pixel should output into the destination pixel. The average pixel color in the source will be the average pixel color in the destination, which these other formulas, I think they will not be.
an example of how to use is at the bottom of
https://github.com/danschumann/limby-resize
UPDATE OCT 2018: These days my example is more academic than anything else. Webgl is pretty much 100%, so you'd be better off resizing with that to produce similar results, but faster. PICA.js does this, I believe. –
其中一些解决方案的问题在于它们直接访问像素数据并循环遍历它以执行下采样。根据图像的大小,这可能会非常消耗资源,最好使用浏览器的内部算法。
drawImage() 函数使用线性插值、最近邻重采样方法。 当您调整的大小不超过原始大小的一半时,效果很好。
如果您循环一次只调整最大一半的大小,结果会非常好,并且比访问像素数据快得多。
此函数一次将采样减少一半,直到达到所需的大小:
归功于 这篇文章
The problem with some of this solutions is that they access directly the pixel data and loop through it to perform the downsampling. Depending on the size of the image this can be very resource intensive, and it would be better to use the browser's internal algorithms.
The drawImage() function is using a linear-interpolation, nearest-neighbor resampling method. That works well when you are not resizing down more than half the original size.
If you loop to only resize max one half at a time, the results would be quite good, and much faster than accessing pixel data.
This function downsample to half at a time until reaching the desired size:
Credits to this post
因此,我不久前在使用画布时发现了一些有趣的东西,这可能会有所帮助:
要自行调整画布控件的大小,您需要使用
height=""
和width=" “
属性(或canvas.width
/canvas.height
元素)。如果您使用 CSS 调整画布大小,它实际上会拉伸(即:调整大小)画布内容以适合整个画布(而不是简单地增加或减少画布面积。值得一试将图像绘制到画布控件中,并将高度和宽度属性设置为图像的大小,然后使用 CSS 将画布大小调整为您想要的大小,也许这也应该使用不同的调整大小算法
。注意到canvas在不同的浏览器中(甚至不同浏览器的不同版本)有不同的效果,浏览器中使用的算法和技术很可能会随着时间的推移而改变(尤其是Firefox 4和Chrome 6这么快就出来了,这会带来沉重的负担)。强调画布渲染性能)
此外,您可能也想尝试一下 SVG,因为它可能也使用不同的算法
祝您好运!
So something interesting that I found a while ago while working with canvas that might be helpful:
To resize the canvas control on its own, you need to use the
height=""
andwidth=""
attributes (orcanvas.width
/canvas.height
elements). If you use CSS to resize the canvas, it will actually stretch (i.e.: resize) the content of the canvas to fit the full canvas (rather than simply increasing or decreasing the area of the canvas.It'd be worth a shot to try drawing the image into a canvas control with the height and width attributes set to the size of the image and then using CSS to resize the canvas to the size you're looking for. Perhaps this would use a different resizing algorithm.
It should also be noted that canvas has different effects in different browsers (and even different versions of different browsers). The algorithms and techniques used in the browsers is likely to change over time (especially with Firefox 4 and Chrome 6 coming out so soon, which will place heavy emphasis on canvas rendering performance).
In addition, you may want to give SVG a shot, too, as it likely uses a different algorithm as well.
Best of luck!
快速简单的 Javascript 图像缩放器:
https://github.com/calvintwr/blitz-hermite-resize
历史
这确实是经过多轮研究、阅读和尝试之后的结果。
缩放器算法使用@ViliusL 的 Hermite 脚本(Hermite 缩放器确实是最快的,并且提供相当好的输出)。扩展了您需要的功能。
分叉 1 个工作线程来调整大小,这样在调整大小时不会冻结浏览器,这与所有其他 JS 调整器不同。
Fast and simple Javascript image resizer:
https://github.com/calvintwr/blitz-hermite-resize
History
This is really after many rounds of research, reading and trying.
The resizer algorithm uses @ViliusL's Hermite script (Hermite resizer is really the fastest and gives reasonably good output). Extended with features you need.
Forks 1 worker to do the resizing so that it doesn't freeze your browser when resizing, unlike all other JS resizers out there.
我将 @syockit 的答案以及逐步下降的方法转换为可重用的 Angular 服务,供任何感兴趣的人使用: https: //gist.github.com/fisch0920/37bac5e741eaec60e983
我包含了这两种解决方案,因为它们都有自己的优点/缺点。 lanczos 卷积方法的质量较高,但速度较慢,而逐步缩小方法可产生合理的抗锯齿结果,并且速度明显更快。
用法示例:
I converted @syockit's answer as well as the step-down approach into a reusable Angular service for anyone who's interested: https://gist.github.com/fisch0920/37bac5e741eaec60e983
I included both solutions because they both have their own pros / cons. The lanczos convolution approach is higher quality at the cost of being slower, whereas the step-wise downscaling approach produces reasonably antialiased results and is significantly faster.
Example usage:
感谢@syockit 的精彩回答。但是,我必须按如下方式重新格式化才能使其正常工作。可能是由于 DOM 扫描问题:
Thanks @syockit for an awesome answer. however, I had to reformat a little as follows to make it work. Perhaps due to DOM scanning issues:
我想从这里的答案中得到一些定义良好的函数,所以最终得到了这些函数,希望对其他人也有用,
只是为了测试这个在选项卡中打开的图像上运行它。
I wanted some well defined functions out of answers here so ended up with these which am hoping would be useful for others also,
Just for the sake of testing this run it on a image opened in a tab.
我刚刚运行了一页并排比较,除非最近发生了一些变化,否则我看不出使用 canvas 与简单 css 相比,没有更好的缩小(缩放)尺寸。我在 FF6 Mac OSX 10.7 上进行了测试。与原版相比仍然稍微柔软。
然而,我确实偶然发现了一些确实产生巨大影响的东西,那就是在支持画布的浏览器中使用图像过滤器。实际上,您可以像在 Photoshop 中一样处理图像,包括模糊、锐化、饱和度、波纹、灰度等。
然后我发现了一个很棒的 jQuery 插件,它使这些滤镜的应用变得轻而易举:
http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation -plugin/428234
我只是在调整图像大小后立即应用锐化滤镜,这应该会给您带来所需的效果。我什至不必使用画布元素。
I just ran a page of side by sides comparisons and unless something has changed recently, I could see no better downsizing (scaling) using canvas vs. simple css. I tested in FF6 Mac OSX 10.7. Still slightly soft vs. the original.
I did however stumble upon something that did make a huge difference and that was using image filters in browsers that support canvas. You can actually manipulate images much like you can in Photoshop with blur, sharpen, saturation, ripple, grayscale, etc.
I then found an awesome jQuery plug-in which makes application of these filters a snap:
http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234
I simply apply the sharpen filter right after resizing the image which should give you the desired effect. I didn't even have to use a canvas element.
正在寻找另一个出色的简单解决方案?
该解决方案将使用浏览器的调整大小算法! :)
Looking for another great simple solution?
This solution will use the resize algorith of browser! :)