KineticJS:如何在不降低质量的情况下编辑比我的屏幕/舞台尺寸大得多的图像?

时间:2014-11-14 00:26:06

标签: javascript kineticjs

我在iPhone 5上有一个KineticJS 5.1.0舞台。由于iPhone的屏幕尺寸,我的舞台以320px x 320px为界。我拍摄的图像是1280像素x 1280像素。我希望能够在舞台上处理这个图像而不会因为收缩而失去质量。

以下是原始图片(大版本:https://dl.dropboxusercontent.com/u/47067729/originalImage2.jpg):

enter image description here

当我将图像加载到舞台中时,会发生以下情况:

enter image description here

图像看起来很奇怪,它在图像中可以看到很多条带。

当我应用过滤器时,图像看起来像这样。

enter image description here

它不应该是什么样子。

我创建了一个显示此问题的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,这会导致质量损失。

1 个答案:

答案 0 :(得分:4)

在iPhone上看到的铃声模式是resizing artifacts,一种Moiré pattern。它是actually pretty common

在撰写时,我们无法控制画布如何缩放图像,因此我们需要解决它:

  1. 使用更合适的scaling algorithm创建一个或多个缩放图像。目的是避免缩放,从而避免任何伪影。

  2. 实施step down algorithm,并在将其交给Kinetic之前生成更合适尺寸的图像。

  3. 在缩小尺寸之前模糊图像会降低伪影,但不会消除它。在Kinetic中,只需正常应用模糊滤镜。

  4. 实施更合适的scaling algorithm,并使用它动态缩放图片到维度(即自动化#1)。

  5. 当然,建议采用前两种方法。


    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]);
    

    原始问题还询问了渲染图像的感知差异,这不太可能是由不同的库引起的。

    根据经验,这些因素通常会影响显示颜色的外观:

    1. 不同的显示器(甚至相同的型号)或具有不同设置或模式的相同显示器将以不同的方式显示颜色。
    2. 屏幕调整软件,例如Adobe Gamma或某些显示卡驱动程序,可能会严重扭曲颜色。
    3. 不同的视角;相同的图像在左侧和右侧或上下都可能看起来不同。