SORU
20 EYLÜL 2013, Cuma


Yeniden Boyutlandırma (aşağı ölçek) Yüksek Kalite HTML5 Tuval Resim?

Html5 tuval öğeleri görüntüler benim tarayıcı im yeniden boyutlandırmak için kullanıyorum. Kalitesi çok düşük olduğu ortaya çıktı. Bunu buldum: Disable Interpolation when Scaling a <canvas> ama kalitesini artırmak için yardımcı olmaz.

Aşağıda benim css ve js kodu olarak resmi Photoshop ile scalled ve canvas API olarak sunulacak.

Ben tarayıcıda bir görüntü Ölçekleme sırasında en iyi kalite elde etmek için ne yapmam gerekiyor?

Not: küçük bir büyük bir görüntüyü küçültün, bir tuval rengini değiştirmek ve sunucu için tuvalden sonucu göndermek istiyorum.

CSS:

canvas, img {
    image-rendering: optimizeQuality;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: optimize-contrast;
    -ms-interpolation-mode: nearest-neighbor;
}

JS:

var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {


   var originalContext = $originalCanvas[0].getContext('2d');   
   originalContext.imageSmoothingEnabled = false;
   originalContext.webkitImageSmoothingEnabled = false;
   originalContext.mozImageSmoothingEnabled = false;
   originalContext.drawImage(this, 0, 0, 379, 500);
});

Resmi photoshop ile yeniden boyutlandırılabilir:

enter image description here

Resim tuval üzerinde yeniden boyutlandırılabilir:

enter image description here

Düzenleme:

Önerilen gibi birden fazla adımda ölçek küçültme yapmaya çalıştım:

Resizing an image in an HTML5 canvas Html5 canvas drawImage: how to apply antialiasing

Bu kullandığım fonksiyon

function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
    var imgWidth = img.width, 
        imgHeight = img.height;

    var ratio = 1, ratio1 = 1, ratio2 = 1;
    ratio1 = maxWidth / imgWidth;
    ratio2 = maxHeight / imgHeight;

    // Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
    if (ratio1 < ratio2) {
        ratio = ratio1;
    }
    else {
        ratio = ratio2;
    }

    var canvasContext = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");
    var canvasCopy2 = document.createElement("canvas");
    var copyContext2 = canvasCopy2.getContext("2d");
    canvasCopy.width = imgWidth;
    canvasCopy.height = imgHeight;  
    copyContext.drawImage(img, 0, 0);

    // init
    canvasCopy2.width = imgWidth;
    canvasCopy2.height = imgHeight;        
    copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);


    var rounds = 2;
    var roundRatio = ratio * rounds;
    for (var i = 1; i <= rounds; i  ) {
        console.log("Step: " i);

        // tmp
        canvasCopy.width = imgWidth * roundRatio / i;
        canvasCopy.height = imgHeight * roundRatio / i;

        copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);

        // copy back
        canvasCopy2.width = imgWidth * roundRatio / i;
        canvasCopy2.height = imgHeight * roundRatio / i;
        copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);

    } // end for


    // copy back to canvas
    canvas.width = imgWidth * roundRatio / rounds;
    canvas.height = imgHeight * roundRatio / rounds;
    canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);


}

Burada ise boyutlandırma aşağı 2 bir adım kullanırsam sonuç:

enter image description here

Burada ise 3 adım aşağı boyutlandırma kullanırsam sonuç:

enter image description here

Burada ise 4 adım aşağı boyutlandırma kullanırsam sonuç:

enter image description here

Burada ise 20 adım aşağı boyutlandırma kullanırsam sonuç:

enter image description here

NOT: 2 Adım 1 Adım görüntü kalitesinde büyük bir gelişme var ama ekleme adımları süreç daha fazla bulanık görüntü haline dönüştü.

Görüntü daha adımları ekleyin daha bulanık olur bir şekilde sorunu çözmek için vardır?

Edit 2013-10-04: GameAlchemist algoritma çalıştım. İşte sonuç Photoshop ile karşılaştırıldığında.

Resim PhotoShop:

PhotoShop Image

GameAlchemist Algoritması:

GameAlchemist's Algorithm

CEVAP
2 EKİM 2013, ÇARŞAMBA


Senin sorunun resim fakir olduğu için, aradeğerleme -piksel. oluşturma hakkında konuşmanın anlamı yok Burada sorun, aşağı örnekleme.
Bir görüntü altörnekleme için, hedef görüntüde tek bir pikselin içine orijinal görüntü p * p piksel her kare açmak zorundayız.
Tarayıcıları çok basit bir azaltma yapmak performanslar sebep : küçük resim oluşturmak için, sadece kaynak olarak BİR piksel almak ve hedef için değerini kullanacaktır. 'forgots' bazı ayrıntılar ve gürültü ekler.
Henüz bir özel durum için bu : yana 2X görüntü aşağı örnekleme çok basit bir hesapla (ortalama 4 piksel için bir tane) ve kullanılan retina/Hidpı piksel, bu durumda işlenen düzgün Tarayıcısı kullanın 4 piksel için tek.
Eğer birkaç kez kullanırsanız AMA... 2X azaltma-birbirini izleyen yuvarlama hataları çok fazla gürültü katacak bir sorun karşısında olacak.
Daha da kötüsü, her zaman iki güç tarafından yeniden boyutlandırmak olmaz, ve en yakın iktidara yeniden boyutlandırma son bir boyutlandırma çok gürültülü.

Aradığınız şey bir piksel mükemmel aşağı örnekleme : hesaba tüm giriş piksel-her neyse ölçek. bu görüntü yeniden örnekleme
Bunu yapmak zorundayız hesaplamak için her giriş piksel, katkısı için bir, iki veya dört hedef piksel olarak hava çaplı projeksiyon giriş piksel içinden bir hedef piksel, örtüşen bir sınır X, Y sınır, ya da her ikisi.
( Düzeni olman iyi olurdu, ama yok. )

İşte bir zombat tuval ölçeği vs bir örnek 1/3 ölçekte piksel mükemmel ölçeği. Resmi tarayıcınızda ölçekli olabilir, dikkat edin .S. Ç.. tarafından jpegized
Bununla birlikte, özellikle kanguru arkasındaki çim, ve onun sağ dallarında çok daha az gürültü var. Kürk gürültü daha tezat yapar, ama aksine kaynak resim-beyaz tüyleri var gibi görünüyor.
Sağ görüntü daha az dikkat çekici ama kesinlikle daha güzel.

enter image description here

İşte piksel mükemmel ölçek küçültme yapmak için kod :

keman sonucu : http://jsfiddle.net/gamealchemist/r6aVp/embedded/result/
keman kendisi : http://jsfiddle.net/gamealchemist/r6aVp/

// scales the image by (float) scale < 1
// returns a canvas containing the scaled image.
function downScaleImage(img, scale) {
    var imgCV = document.createElement('canvas');
    imgCV.width = img.width;
    imgCV.height = img.height;
    var imgCtx = imgCV.getContext('2d');
    imgCtx.drawImage(img, 0, 0);
    return downScaleCanvas(imgCV, scale);
}

// scales the canvas by (float) scale < 1
// returns a new canvas containing the scaled image.
function downScaleCanvas(cv, scale) {
    if (!(scale < 1) || !(scale > 0)) throw ('scale must be a positive number <1 ');
    var sqScale = scale * scale; // square scale = area of source pixel within target
    var sw = cv.width; // source image width
    var sh = cv.height; // source image height
    var tw = Math.floor(sw * scale); // target image width
    var th = Math.floor(sh * scale); // target image height
    var sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
    var tx = 0, ty = 0, yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
    var tX = 0, tY = 0; // rounded tx, ty
    var w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
    // weight is weight of current source point within target.
    // next weight is weight of current source point within next target's point.
    var crossX = false; // does scaled px cross its current px right border ?
    var crossY = false; // does scaled px cross its current px bottom border ?
    var sBuffer = cv.getContext('2d').
    getImageData(0, 0, sw, sh).data; // source buffer 8 bit rgba
    var tBuffer = new Float32Array(3 * tw * th); // target buffer Float32 rgb
    var sR = 0, sG = 0,  sB = 0; // source's current point r,g,b
    /* untested !
    var sA = 0;  //source alpha  */    

    for (sy = 0; sy < sh; sy  ) {
        ty = sy * scale; // y src position within target
        tY = 0 | ty;     // rounded : target pixel's y
        yIndex = 3 * tY * tw;  // line index within target array
        crossY = (tY != (0 | ty   scale)); 
        if (crossY) { // if pixel is crossing botton target pixel
            wy = (tY   1 - ty); // weight of point within target pixel
            nwy = (ty   scale - tY - 1); // ... within y 1 target pixel
        }
        for (sx = 0; sx < sw; sx  , sIndex  = 4) {
            tx = sx * scale; // x src position within target
            tX = 0 |  tx;    // rounded : target pixel's x
            tIndex = yIndex   tX * 3; // target pixel index within target array
            crossX = (tX != (0 | tx   scale));
            if (crossX) { // if pixel is crossing target pixel's right
                wx = (tX   1 - tx); // weight of point within target pixel
                nwx = (tx   scale - tX - 1); // ... within x 1 target pixel
            }
            sR = sBuffer[sIndex    ];   // retrieving r,g,b for curr src px.
            sG = sBuffer[sIndex   1];
            sB = sBuffer[sIndex   2];

            /* !! untested : handling alpha !!
               sA = sBuffer[sIndex   3];
               if (!sA) continue;
               if (sA != 0xFF) {
                   sR = (sR * sA) >> 8;  // or use /256 instead ??
                   sG = (sG * sA) >> 8;
                   sB = (sB * sA) >> 8;
               }
            */
            if (!crossX && !crossY) { // pixel does not cross
                // just add components weighted by squared scale.
                tBuffer[tIndex    ]  = sR * sqScale;
                tBuffer[tIndex   1]  = sG * sqScale;
                tBuffer[tIndex   2]  = sB * sqScale;
            } else if (crossX && !crossY) { // cross on X only
                w = wx * scale;
                // add weighted component for current px
                tBuffer[tIndex    ]  = sR * w;
                tBuffer[tIndex   1]  = sG * w;
                tBuffer[tIndex   2]  = sB * w;
                // add weighted component for next (tX 1) px                
                nw = nwx * scale
                tBuffer[tIndex   3]  = sR * nw;
                tBuffer[tIndex   4]  = sG * nw;
                tBuffer[tIndex   5]  = sB * nw;
            } else if (crossY && !crossX) { // cross on Y only
                w = wy * scale;
                // add weighted component for current px
                tBuffer[tIndex    ]  = sR * w;
                tBuffer[tIndex   1]  = sG * w;
                tBuffer[tIndex   2]  = sB * w;
                // add weighted component for next (tY 1) px                
                nw = nwy * scale
                tBuffer[tIndex   3 * tw    ]  = sR * nw;
                tBuffer[tIndex   3 * tw   1]  = sG * nw;
                tBuffer[tIndex   3 * tw   2]  = sB * nw;
            } else { // crosses both x and y : four target points involved
                // add weighted component for current px
                w = wx * wy;
                tBuffer[tIndex    ]  = sR * w;
                tBuffer[tIndex   1]  = sG * w;
                tBuffer[tIndex   2]  = sB * w;
                // for tX   1; tY px
                nw = nwx * wy;
                tBuffer[tIndex   3]  = sR * nw;
                tBuffer[tIndex   4]  = sG * nw;
                tBuffer[tIndex   5]  = sB * nw;
                // for tX ; tY   1 px
                nw = wx * nwy;
                tBuffer[tIndex   3 * tw    ]  = sR * nw;
                tBuffer[tIndex   3 * tw   1]  = sG * nw;
                tBuffer[tIndex   3 * tw   2]  = sB * nw;
                // for tX   1 ; tY  1 px
                nw = nwx * nwy;
                tBuffer[tIndex   3 * tw   3]  = sR * nw;
                tBuffer[tIndex   3 * tw   4]  = sG * nw;
                tBuffer[tIndex   3 * tw   5]  = sB * nw;
            }
        } // end for sx 
    } // end for sy

    // create result canvas
    var resCV = document.createElement('canvas');
    resCV.width = tw;
    resCV.height = th;
    var resCtx = resCV.getContext('2d');
    var imgRes = resCtx.getImageData(0, 0, tw, th);
    var tByteBuffer = imgRes.data;
    // convert float32 array into a UInt8Clamped Array
    var pxIndex = 0; //  
    for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex  = 3, tIndex  = 4, pxIndex  ) {
        tByteBuffer[tIndex] = Math.ceil(tBuffer[sIndex]);
        tByteBuffer[tIndex   1] = Math.ceil(tBuffer[sIndex   1]);
        tByteBuffer[tIndex   2] = Math.ceil(tBuffer[sIndex   2]);
        tByteBuffer[tIndex   3] = 255;
    }
    // writing result to canvas.
    resCtx.putImageData(imgRes, 0, 0);
    return resCV;
}

oldukçayüzen bir arabellek hedef görüntünün Ara değerleri saklamak için gerekli olduğundan bellek açgözlü, ( ->eğer sonuç tuval sayacak olursak, bu algoritma içinde 6 kez kaynak görüntü bellek) kullanıyoruz.
Her kaynak piksel hedef boyutu ne olursa olsun kullanılan bu yana, aynı zamanda oldukça da pahalı, ve getİmageData / putİmageDate, oldukça yavaş da ödemek zorundayız.
Ama imkan yok olması daha hızlı işlem her bir kaynak değeri bu durumda, durum o kadar da kötü değil : benim 740 * 556 görüntünün bir wombat, süreç, 30 ve 40 yaş arası ms.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • andyabc45

    andyabc45

    1 Mayıs 2011
  • EatingIn

    EatingIn

    12 EKİM 2013
  • Michelle Phan

    Michelle Pha

    18 Temmuz 2006