ste*_*eve 72 javascript html5 canvas image html5-canvas
我试图用帆布调整一些图像的大小,但我对如何平滑它们毫无头绪.在Photoshop,浏览器等..他们使用了一些算法(例如双三次,双线性)但我不知道它们是否内置于画布中.
这是我的小提琴:http://jsfiddle.net/EWupT/
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width=300
canvas.height=234
ctx.drawImage(img, 0, 0, 300, 234);
document.body.appendChild(canvas);
Run Code Online (Sandbox Code Playgroud)
第一个是普通的已调整大小的图像标记,第二个是画布.请注意画布如何不那么平滑.我怎样才能实现'平滑'?
小智 118
您可以使用向下步进来获得更好的结果.在调整图像大小时,大多数浏览器似乎使用线性插值而不是双三次.
(更新已为规格添加了质量属性,imageSmoothingQuality
目前仅在Chrome中提供.)
除非选择平滑或最近邻居,否则浏览器将在缩小图像后始终对图像进行插值,因为此功能作为低通滤波器以避免混叠.
双线性使用2x2像素进行插值,而双立方使用4x4,因此通过逐步进行,您可以接近双三次结果,同时使用双线性插值,如结果图像中所示.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function () {
// set size proportional to image
canvas.height = canvas.width * (img.height / img.width);
// step 1 - resize to 50%
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);
// step 2
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
// step 3, resize to final size
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
}
img.src = "//i.imgur.com/SHo6Fub.jpg";
Run Code Online (Sandbox Code Playgroud)
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234">
<canvas id="canvas" width=300></canvas>
Run Code Online (Sandbox Code Playgroud)
根据您调整大小的程度,如果差异较小,您可以跳过第2步.
在演示中,您可以看到新结果现在与图像元素非常相似.
fis*_*ch2 17
我创建了一个可重复使用的Angular服务来为任何感兴趣的人处理高质量的图像/画布大小调整:https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983
该服务包括两个解决方案,因为它们都有自己的优点/缺点.lanczos卷积方法质量较高,代价是较慢,而逐步降尺度方法产生合理的抗锯齿结果,并且速度明显更快.
用法示例:
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
})
})
Run Code Online (Sandbox Code Playgroud)
小智 17
由于Trung Le Nguyen Nhat的小提琴根本不正确(它只是在最后一步使用了原始图像),
我写了自己的一般小提琴,进行了性能比较:
基本上它是:
img.onload = function() {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
oc = document.createElement('canvas'),
octx = oc.getContext('2d');
canvas.width = width; // destination canvas size
canvas.height = canvas.width * img.height / img.width;
var cur = {
width: Math.floor(img.width * 0.5),
height: Math.floor(img.height * 0.5)
}
oc.width = cur.width;
oc.height = cur.height;
octx.drawImage(img, 0, 0, cur.width, cur.height);
while (cur.width * 0.5 > width) {
cur = {
width: Math.floor(cur.width * 0.5),
height: Math.floor(cur.height * 0.5)
};
octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height);
}
ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height);
}
Run Code Online (Sandbox Code Playgroud)
小智 11
虽然其中一些代码片段简短且有效,但遵循和理解它们并非易事。
由于我不是堆栈溢出中“复制粘贴”的粉丝,我希望开发人员了解他们推送到他们软件中的代码,希望您会发现以下有用。
演示:使用 JS 和 HTML Canvas Demo fiddler 调整图像大小。
您可能会找到 3 种不同的方法来调整大小,这将帮助您了解代码的工作方式以及原因。
https://jsfiddle.net/1b68eLdr/93089/
可以在 GitHub 项目中找到演示的完整代码以及您可能希望在代码中使用的 TypeScript 方法。
https://github.com/eyalc4/ts-image-resizer
这是最终的代码:
export class ImageTools {
base64ResizedImage: string = null;
constructor() {
}
ResizeImage(base64image: string, width: number = 1080, height: number = 1080) {
let img = new Image();
img.src = base64image;
img.onload = () => {
// Check if the image require resize at all
if(img.height <= height && img.width <= width) {
this.base64ResizedImage = base64image;
// TODO: Call method to do something with the resize image
}
else {
// Make sure the width and height preserve the original aspect ratio and adjust if needed
if(img.height > img.width) {
width = Math.floor(height * (img.width / img.height));
}
else {
height = Math.floor(width * (img.height / img.width));
}
let resizingCanvas: HTMLCanvasElement = document.createElement('canvas');
let resizingCanvasContext = resizingCanvas.getContext("2d");
// Start with original image size
resizingCanvas.width = img.width;
resizingCanvas.height = img.height;
// Draw the original image on the (temp) resizing canvas
resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height);
let curImageDimensions = {
width: Math.floor(img.width),
height: Math.floor(img.height)
};
let halfImageDimensions = {
width: null,
height: null
};
// Quickly reduce the dize by 50% each time in few iterations until the size is less then
// 2x time the target size - the motivation for it, is to reduce the aliasing that would have been
// created with direct reduction of very big image to small image
while (curImageDimensions.width * 0.5 > width) {
// Reduce the resizing canvas by half and refresh the image
halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5);
halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5);
resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height,
0, 0, halfImageDimensions.width, halfImageDimensions.height);
curImageDimensions.width = halfImageDimensions.width;
curImageDimensions.height = halfImageDimensions.height;
}
// Now do final resize for the resizingCanvas to meet the dimension requirments
// directly to the output canvas, that will output the final image
let outputCanvas: HTMLCanvasElement = document.createElement('canvas');
let outputCanvasContext = outputCanvas.getContext("2d");
outputCanvas.width = width;
outputCanvas.height = height;
outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height,
0, 0, width, height);
// output the canvas pixels as an image. params: format, quality
this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85);
// TODO: Call method to do something with the resize image
}
};
}}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么没有人建议createImageBitmap
。
createImageBitmap(
document.getElementById('image'),
{ resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' }
)
.then(imageBitmap =>
document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0)
);
Run Code Online (Sandbox Code Playgroud)
效果很好(假设您为图像和画布设置了 id)。
我创建了一个库,允许您在保留所有颜色数据的同时降低任何百分比。
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
您可以在浏览器中包含该文件。结果看起来像photoshop或image magick,保留所有颜色数据,平均像素,而不是取附近的并丢弃其他的。它不使用公式来猜测平均值,而是采用精确的平均值。
基于 K3N 答案,我一般会为任何想要的人重写代码
var oc = document.createElement('canvas'), octx = oc.getContext('2d');
oc.width = img.width;
oc.height = img.height;
octx.drawImage(img, 0, 0);
while (oc.width * 0.5 > width) {
oc.width *= 0.5;
oc.height *= 0.5;
octx.drawImage(oc, 0, 0, oc.width, oc.height);
}
oc.width = width;
oc.height = oc.width * img.height / img.width;
octx.drawImage(img, 0, 0, oc.width, oc.height);
Run Code Online (Sandbox Code Playgroud)
更新 JSFiddle 演示
这是我的在线演示
归档时间: |
|
查看次数: |
221236 次 |
最近记录: |