我在iPhone 5上有一个KineticJS 5.1.0舞台。由于iPhone的屏幕尺寸,我的舞台以320px x 320px为界。我拍摄的图像是1280像素x 1280像素。我希望能够在舞台上处理这个图像而不会因为收缩而失去质量。
以下是原始图片(大版本:https://dl.dropboxusercontent.com/u/47067729/originalImage2.jpg):
当我将图像加载到舞台中时,会发生以下情况:
图像看起来很奇怪,它在图像中可以看到很多条带。
当我应用过滤器时,图像看起来像这样。
它不应该是什么样子。
我创建了一个显示此问题的JsFiddle(http://jsfiddle.net/confile/qhm7dn09/)
console.clear();
var imageObj = new Image();
var img = null;
var saveBtn = document.getElementById("save");
var resultImage = document.getElementById("resultImage");
var original = document.getElementById("original");
var downloadLink = document.getElementById("DownloadLink");
Kinetic.pixelRatio = 4;
stage = new Kinetic.Stage({
container: 'container',
width: 320,
height: 320
});
layer = new Kinetic.Layer();
img = new Kinetic.Image({
x: 0,
y: 0,
image: new Image()
});
layer.add(img);
stage.add(layer);
imageObj.onload = function() {
var tmpWidth = Math.floor(imageObj.width /2);
var w = Math.min(tmpWidth, 320);
var h = imageObj.height * w / imageObj.width;
img.setImage(this);
img.setWidth(w);
img.setHeight(h);
img.cache(); // 5.1.0
// img.setFilter(Kinetic.Filters.Shinny); // 4.5.2.
img.filters([Kinetic.Filters.Shinny]); // 5.1.0
layer.draw();
};
imageObj.crossOrigin = "anonymous";
var imageUrl = "https://dl.dropboxusercontent.com/u/47067729/originalImage2.jpg";
imageObj.src = imageUrl;
original.src = imageUrl;
saveBtn.onclick = function(evt) {
console.log("save");
var canvas = $(stage.getContent()).find('canvas').get(0);
console.log("canvas: "+canvas);
var dataURL = canvas.toDataURL('image/jpeg', 1.0);
resultImage.src = dataURL;
downloadLink.href = dataURL;
};
如何在不降低质量的情况下编辑比我的屏幕/舞台尺寸大得多的图像?
编辑:呈现给用户的舞台/画布仍然是某种预览。原始图像不应缩小尺寸。应用效果并导出舞台后,我希望必须将导出的图像与原始图像大小相同,而不降低质量。我不想将320px x 320px阶段的大小调整为1280px x 1280px,这会导致质量损失。
答案 0 :(得分:4)
在iPhone上看到的铃声模式是resizing artifacts,一种Moiré pattern。它是actually pretty common。
在撰写时,我们无法控制画布如何缩放图像,因此我们需要解决它:
使用更合适的scaling algorithm创建一个或多个缩放图像。目的是避免缩放,从而避免任何伪影。
实施step down algorithm,并在将其交给Kinetic之前生成更合适尺寸的图像。
在缩小尺寸之前模糊图像会降低伪影,但不会消除它。在Kinetic中,只需正常应用模糊滤镜。
实施更合适的scaling algorithm,并使用它动态缩放图片到维度(即自动化#1)。
当然,建议采用前两种方法。
320px阶段只会导出320像素的图像。要以原始大小导出原始图像而不进行缩放,您需要从足够大的舞台导出。 新阶段不需要是可见的;这是off-screen rendering。
我修改了小提琴的保存处理程序来演示它:http://jsfiddle.net/qhm7dn09/13/embedded/result
请记住,JavaScript无法像本机程序一样快速或高效,大多数移动设备都很难使用JS在浏览器中处理大图像。
最初的问题是如何改善缩小的图像质量。
鉴于库存模糊过滤器,为Kinetic 5.1实现锐化过滤器相对容易。具体来说,是一个非锐化的掩码过滤器。
Kinetic.Filters.UnsharpMask = function kinetic_filter_unsharpmask( imageData ) {
var amount = this.attrs.unsharpAmout / 100; // Normally 1 to 100 (%)
var radius = Math.round( this.attrs.unsharpRadius );
var threshold = Math.round( this.attrs.unsharpThreshold ); // 1 to 765
if ( amount && radius > 0 && threshold > 0 ) {
var data = imageData.data, len = data.length, i;
// Copy the image and call blur filter on it
var blurData = new Uint8Array( imageData.data.buffer.slice(0) );
Kinetic.Filters.Blur.call(
{ blurRadius: function(){ return radius; } },
{ data: blurData, width: imageData.width, height: imageData.height } );
// Unsharp mask
for ( i = 0 ; i < len ; i += 4 ) {
// Calculate difference
var d = [ data[i] - blurData[i], data[i+1] - blurData[i+1], data[i+2] - blurData[i+2] ];
var diff = Math.abs( d[0] ) + Math.abs( d[1] ) + Math.abs( d[2] );
// Apply difference
if ( diff && diff >= threshold ) {
data[i ] = Math.max( 0, Math.min( Math.round( data[i ] + d[0] * amount ), 255 ) );
data[i+1] = Math.max( 0, Math.min( Math.round( data[i+1] + d[1] * amount ), 255 ) );
data[i+2] = Math.max( 0, Math.min( Math.round( data[i+2] + d[2] * amount ), 255 ) );
}
}
}
}
用法:
img = new Kinetic.Image({
x: 0, y: 0, image: imageObj,
unsharpRadius: 3, // Pixel range, should be integer (blur filter will round it to integer)
unsharpAmout: 50, // 1 to 100
unsharpThreshold: 10 // 1 to 765
});
img.filters([Kinetic.Filters.UnsharpMask]);
原始问题还询问了渲染图像的感知差异,这不太可能是由不同的库引起的。
根据经验,这些因素通常会影响显示颜色的外观: