KineticJS: How can I edit an image which is much bigger than my screen/stage size without loosing quality?

326 views Asked by At

I have a KineticJS 5.1.0 stage on an iPhone 5. Due to the screen size of the iPhone my stage is bounded by 320px x 320px. The image I capture is 1280px x 1280px. I want to be able to process this image on the stage without loosing quality due to shrinking.

Here is the original image (Big version: https://dl.dropboxusercontent.com/u/47067729/originalImage2.jpg):

enter image description here

Here is what happens, when I load the image into the stage:

enter image description here

The image looks very strange it has a lot of strips inside as you can see on the image.

When I apply a filter the image looks like this.

enter image description here

Which is not what it should look like.

I created a JsFiddle (http://jsfiddle.net/confile/qhm7dn09/) which shows this problem

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;

};

How can I edit an image which is much bigger than my screen/stage size without loosing quality?

Edit: The stage/canvas which is present to the user is still some kind of preview. The original image should not be downscaled. After applying effects and exporting the stage I want to have to exported image to be of the same size as the original one without quality reduction. I do not want to resize a 320px x 320px stage back to 1280px x 1280px which would cause a hugh quality loss.

1

There are 1 answers

6
Sheepy On BEST ANSWER

The ringing pattern seen on iPhone is a resizing artifacts, a kind of Moiré pattern. It is actually pretty common.

As of writing, we have no control on how canvas scale our images, so we need to work around it:

  1. Maunally create one or more scaled images using a more suitable scaling algorithm. The aim is to avoid scaling, and thus avoid any artifacts.

  2. Implement a step down algorithm, and produce an image of more suitable dimension before handing it to Kinetic.

  3. Blurring the image before downscaling will tone down the artifacts, but will not get rid of it. In Kinetic just apply the Blur filter normally.

  4. Implement a more suitable scaling algorithm, and use it to dynamically scale image to dimension (i.e. an automated #1).

Naturally, the first two approachs are recommanded.


A 320px stage will only export a 320px image. To export the original image in its original size without scaling, you need to export from a big enough stage. The new stage does not need to be visible; this is off-screen rendering.

I have modified the fiddle's save handler to demonstrate it: http://jsfiddle.net/qhm7dn09/13/embedded/result

Keep in mind that JavaScript is nowhere as fast or as efficient as native programs, and most mobile will have a hard time processing big image in a browser using JS.


The original question asked how to improve downscaled image quality.

Given the stock blur filter, it is relatively easy to implement a sharpen filter for Kinetic 5.1. Specifically, an unsharp mask filter.

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

Usage:

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

The original question also asked about perceived differences in rendered image, which is unlikely to be caused by different library.

From experience, these factors commonly affect the appearance of displayed colour:

  1. Different monitors (even same model), or same monitor with different settings or modes, will display colours differently.
  2. Screen adjustment software, such as Adobe Gamma or some display card drivers, can considerably distort colours.
  3. Different view angle; same image can look different on the left and on the right, or up and down.