HTML5 Canvas Resize (Downscale) obraz wysokiej jakości?

Używam elementów canvas html5 do zmiany rozmiaru obrazów w przeglądarce. Okazuje się, że jakość jest bardzo niska. Znalazłem to: Wyłącz interpolację podczas skalowania , ale nie pomaga to zwiększyć jakości.

Poniżej znajduje się mój kod css i js, a także obraz skalowany za pomocą Photoshopa i skalowany w canvas API.

Co muszę zrobić, aby uzyskać optymalną jakość podczas skalowania obrazu w przeglądarce?

Uwaga: chcę przeskalować duży obraz do małego, zmodyfikuj kolor w obszarze roboczym i wyślij wynik z obszaru roboczego do serwera.

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

Obraz zmieniany za pomocą Photoshopa:

Tutaj wpisz opis obrazka

Obraz zmieniany na płótnie:

Tutaj wpisz opis obrazka

Edit:

Próbowałem zrobić downscaling w więcej niż jednym kroku, jak zaproponowano w:

Zmiana rozmiaru obrazu w kanwie HTML5 i Html5 canvas drawImage: jak zastosować antyaliasing

To jest funkcja, której użyłem:

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


}

Oto wynik, jeśli użyję rozmiaru 2 stopni w dół:

Tutaj wpisz opis obrazka

Oto wynik, jeśli użyję rozmiaru 3 stopni w dół:

Tutaj wpisz opis obrazka

Oto wynik, jeśli użyję rozmiaru 4 stopni w dół:

Tutaj wpisz opis obrazka

Oto wynik, jeśli użyję rozmiaru 20 stopni w dół:

Tutaj wpisz opis obrazka

Uwaga: okazuje się, że od 1 do 2 kroków jest duża poprawa jakości obrazu, ale im więcej kroków dodasz do przetwarzaj, im bardziej rozmyty staje się obraz.

Czy istnieje sposób na rozwiązanie problemu, że obraz staje się bardziej rozmyty, im więcej kroków dodasz?

Edit 2013-10-04: próbowałem algorytmu GameAlchemist. Oto wynik w porównaniu do Photoshopa.

PhotoShop Image:

PhotoShop Image

Algorytm GameAlchemist:

Algorytm GameAlchemist

Author: Community, 2013-09-20

10 answers

Ponieważ Twoim problemem jest pomniejszenie obrazu, nie ma sensu mówić o interpolacji - czyli tworzeniu pikseli -. Problem tutaj jest downsampling.

Aby zmniejszyć próbkę obrazu, musimy obrócić każdy kwadrat pikseli p * p w oryginalnym obrazie w pojedynczy piksel w obrazie docelowym.

Ze względu na wydajność przeglądarki robią bardzo prosty downsampling : aby zbudować mniejszy obraz, po prostu wybierają jeden piksel w źródle i wykorzystują jego wartość do miejsce. który "zapomina" o szczegółach i dodaje szumu.

Jednak jest od tego wyjątek : ponieważ 2x obraz downsampling jest bardzo prosty do obliczenia (średnio 4 piksele, aby jeden) i jest używany dla retina / HiDPI pikseli, ta sprawa jest obsługiwana poprawnie-przeglądarka korzysta z 4 pikseli, aby jeden-.

Ale... jeśli użyjesz kilkukrotnego próbkowania 2x w dół, napotkasz problem, że kolejne błędy zaokrąglania dodadzą zbyt dużo szumu.
Co gorsza, nie zawsze zmiana rozmiaru o moc dwóch i zmiana rozmiaru do najbliższej mocy + Ostatnia zmiana rozmiaru jest bardzo głośna.

To, czego szukasz, to idealne dla Pikseli próbkowanie w dół, czyli ponowne próbkowanie obrazu, które uwzględni wszystkie piksele wejściowe-niezależnie od skali-.
Aby to zrobić, musimy obliczyć, dla każdego piksela wejściowego, jego wkład do jednego, dwóch lub czterech pikseli docelowych w zależności od tego, czy skalowana projekcja pikseli wejściowych znajduje się w pikselach docelowych, nakładając się na granicę X, Y granica, albo jedno i drugie.
(Schemat byłby tu miły, ale ja go nie mam. )

Oto przykład skali canvas vs my pixel perfect scale w skali 1/3 zombata.

Zauważ, że obraz może zostać przeskalowany w Twojej przeglądarce i jest .jpegized by S. O..
Jednak widzimy, że jest znacznie mniej hałasu, zwłaszcza w trawie za wombatem i gałęziach po jego prawej stronie. Szum w futrze sprawia, że jest bardziej kontrastowy, ale wygląda na to, że ma białe włosy-w przeciwieństwie do źródła zdjęcie-.
Prawy obraz jest mniej chwytliwy, ale zdecydowanie ładniejszy.

Tutaj wpisz opis obrazka

Oto kod do perfekcyjnego skalowania pikseli w dół:

Wynik Fiddle : http://jsfiddle.net/gamealchemist/r6aVp/embedded/result/
/ align = "left" / 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;
}

Jest to całkiem zachłanna pamięć, ponieważ do przechowywania wartości pośrednich obrazu docelowego wymagany jest bufor zmiennoprzecinkowy (- >jeśli zliczymy wynik canvas, używamy 6 razy pamięci obrazu źródłowego w tym algorytmie).
Jest to również dość drogie, ponieważ każdy piksel źródłowy jest używany niezależnie od rozmiaru miejsca docelowego , a my musimy płacić za getImageData / putImageDate, również dość wolno.
Ale nie ma sposobu, aby być szybszym niż przetwarzanie każdej wartości źródłowej w tym przypadku, a sytuacja nie jest taka zła : dla mojego obrazu 740 * 556 wombata przetwarzanie trwa od 30 do 40 ms.

 158
Author: GameAlchemist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-01 20:04:32

Szybka próba płótna z dobrą jakością: http://jsfiddle.net/9g9Nv/442/

Update: version 2.0 (faster, web workers + transferable objects) - https://github.com/viliusle/Hermite-resize

/**
 * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
 * 
 * @param {HtmlElement} canvas
 * @param {int} width
 * @param {int} height
 * @param {boolean} resize_canvas if true, canvas will be resized. Optional.
 */
function resample_single(canvas, width, height, resize_canvas) {
    var width_source = canvas.width;
    var height_source = canvas.height;
    width = Math.round(width);
    height = Math.round(height);

    var ratio_w = width_source / width;
    var ratio_h = height_source / height;
    var ratio_w_half = Math.ceil(ratio_w / 2);
    var ratio_h_half = Math.ceil(ratio_h / 2);

    var ctx = canvas.getContext("2d");
    var img = ctx.getImageData(0, 0, width_source, height_source);
    var img2 = ctx.createImageData(width, height);
    var data = img.data;
    var data2 = img2.data;

    for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
            var x2 = (i + j * width) * 4;
            var weight = 0;
            var weights = 0;
            var weights_alpha = 0;
            var gx_r = 0;
            var gx_g = 0;
            var gx_b = 0;
            var gx_a = 0;
            var center_y = (j + 0.5) * ratio_h;
            var yy_start = Math.floor(j * ratio_h);
            var yy_stop = Math.ceil((j + 1) * ratio_h);
            for (var yy = yy_start; yy < yy_stop; yy++) {
                var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
                var center_x = (i + 0.5) * ratio_w;
                var w0 = dy * dy; //pre-calc part of w
                var xx_start = Math.floor(i * ratio_w);
                var xx_stop = Math.ceil((i + 1) * ratio_w);
                for (var xx = xx_start; xx < xx_stop; xx++) {
                    var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
                    var w = Math.sqrt(w0 + dx * dx);
                    if (w >= 1) {
                        //pixel too far
                        continue;
                    }
                    //hermite filter
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    var pos_x = 4 * (xx + yy * width_source);
                    //alpha
                    gx_a += weight * data[pos_x + 3];
                    weights_alpha += weight;
                    //colors
                    if (data[pos_x + 3] < 255)
                        weight = weight * data[pos_x + 3] / 250;
                    gx_r += weight * data[pos_x];
                    gx_g += weight * data[pos_x + 1];
                    gx_b += weight * data[pos_x + 2];
                    weights += weight;
                }
            }
            data2[x2] = gx_r / weights;
            data2[x2 + 1] = gx_g / weights;
            data2[x2 + 2] = gx_b / weights;
            data2[x2 + 3] = gx_a / weights_alpha;
        }
    }
    //clear and resize canvas
    if (resize_canvas === true) {
        canvas.width = width;
        canvas.height = height;
    } else {
        ctx.clearRect(0, 0, width_source, height_source);
    }

    //draw
    ctx.putImageData(img2, 0, 0);
}
 43
Author: ViliusL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-15 21:17:17

Sugestia 1-rozszerz rurę procesową

Możesz użyć stopniowania, jak opisuję w linkach, do których się odwołujesz, ale wydaje się, że używasz ich w niewłaściwy sposób.

Krok w dół nie jest potrzebny do skalowania obrazów do proporcji powyżej 1: 2 (zazwyczaj, ale nie wyłącznie). To miejsce, w którym musisz wykonać drastyczne skalowanie w dół, musisz podzielić je na dwa (i rzadko więcej) kroki w zależności od zawartości obrazu (w szczególności w przypadku wysokich częstotliwości, takich jak cienkie linie występuje).

Za każdym razem, gdy próbkujesz obraz, tracisz szczegóły i informacje. Nie można oczekiwać, że wynikowy obraz będzie tak wyraźny jak oryginał.

Jeśli skalujesz obrazy w wielu krokach, stracisz w sumie wiele informacji, a wynik będzie słaby, jak już zauważyłeś.

Spróbuj jeszcze jednym krokiem, albo najwyżej dwoma.

Convolutions

W przypadku Photoshopa zauważ, że stosuje on splot po tym, jak obraz ma zostały ponownie pobrane próbki, takie jak sharpen. Nie chodzi tylko o interpolację dwu-sześcienną, więc aby w pełni emulować Photoshopa, musimy również dodać kroki, które wykonuje Photoshop (z domyślną konfiguracją).

W tym przykładzie użyję mojej oryginalnej odpowiedzi, do której odnosisz się w swoim poście, ale dodałem do niej splot wyostrzający, aby poprawić jakość jako proces postu(patrz demo na dole).

Oto kod do dodania filtra sharpen (bazuje on na ogólnym filtrze splotowym-wstawiam macierz wagowa do wyostrzania wewnątrz niej, a także czynnik mieszający do regulacji wymowy efektu):

Użycie:

sharpen(context, width, height, mixFactor);

mixFactor jest wartością pomiędzy [0.0, 1.0] i pozwala na pomniejszenie Zasady wyostrzania efektu: im mniejszy rozmiar, tym mniejszy efekt jest potrzebny.

Function (based on this snippet):

function sharpen(ctx, w, h, mix) {

    var weights =  [0, -1, 0,  -1, 5, -1,  0, -1, 0],
        katet = Math.round(Math.sqrt(weights.length)),
        half = (katet * 0.5) |0,
        dstData = ctx.createImageData(w, h),
        dstBuff = dstData.data,
        srcBuff = ctx.getImageData(0, 0, w, h).data,
        y = h;

    while(y--) {

        x = w;

        while(x--) {

            var sy = y,
                sx = x,
                dstOff = (y * w + x) * 4,
                r = 0, g = 0, b = 0, a = 0;

            for (var cy = 0; cy < katet; cy++) {
                for (var cx = 0; cx < katet; cx++) {

                    var scy = sy + cy - half;
                    var scx = sx + cx - half;

                    if (scy >= 0 && scy < h && scx >= 0 && scx < w) {

                        var srcOff = (scy * w + scx) * 4;
                        var wt = weights[cy * katet + cx];

                        r += srcBuff[srcOff] * wt;
                        g += srcBuff[srcOff + 1] * wt;
                        b += srcBuff[srcOff + 2] * wt;
                        a += srcBuff[srcOff + 3] * wt;
                    }
                }
            }

            dstBuff[dstOff] = r * mix + srcBuff[dstOff] * (1 - mix);
            dstBuff[dstOff + 1] = g * mix + srcBuff[dstOff + 1] * (1 - mix);
            dstBuff[dstOff + 2] = b * mix + srcBuff[dstOff + 2] * (1 - mix)
            dstBuff[dstOff + 3] = srcBuff[dstOff + 3];
        }
    }

    ctx.putImageData(dstData, 0, 0);
}

Wynikiem użycia tej kombinacji będzie:

DEMO ONLINE Tutaj

Wynik w dół i wyostrzyć splot

W zależności od tego, ile ostrzenia chcesz dodać do mieszanki, możesz uzyskać wynik z domyślnego "rozmytego"do bardzo ostrego:

Odmiany wyostrzania

Sugestia 2 - implementacja algorytmu niskiego poziomu

Jeśli chcesz uzyskać najlepszy wynik pod względem jakości, musisz przejść niski poziom i rozważyć wdrożenie na przykład tego nowego algorytmu, aby to zrobić.

Zobacz Interpolacja-dependent Image Downsampling (2011) z IEEE.
oto link do artykułu w całości (PDF) .

Nie ma implementacji tego algorytmu w JavaScript AFAIK w tej chwili, więc jesteś na pełne ręce, jeśli chcesz rzucić się na to zadanie.

Istotą jest (fragmenty artykułu):

Abstract

Proponowany jest algorytm adaptacyjnego próbkowania w dół zorientowanego interpolacyjnie do kodowania obrazów o niskiej przepływności w tym artykule. Given an image, the proponowany algorytm jest w stanie uzyskać obraz o niskiej rozdzielczości, z który obraz wysokiej jakości o tej samej rozdzielczości co wejście obraz może być interpolowany. Różni się od tradycyjnych algorytmy down-samplingu, które są niezależne od procesu interpolacji, proponowany algorytm próbkowania w dół pobieranie próbek w dół do procesu interpolacji. W związku z tym proponowany algorytm down-samplingu jest w stanie utrzymać oryginalny informacje o obrazie wejściowym w największym stopniu. The down-sampled obraz jest następnie przesyłany do formatu JPEG. A total variation (TV) based post przetwarzanie jest następnie stosowane do zdekompresowanego obrazu o niskiej rozdzielczości. Ostatecznie przetworzony obraz jest interpolowany w celu utrzymania oryginalna rozdzielczość obrazu wejściowego. wyniki eksperymentalne że wykorzystując obraz downsamplowany przez proponowany algorytm, można uzyskać obraz interpolowany o znacznie wyższej jakości. Poza tym, proponowany algorytm jest w stanie osiągnąć lepsze wydajność niż JPEG do kodowania obrazu o niskiej przepływności.

Migawka z papieru

(Zobacz podany link dla wszystkich szczegółów, formuł itp.)

 30
Author: epistemex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-10 04:31:50

Po co używać płótna do zmiany rozmiaru obrazów? Współczesne przeglądarki używają interpolacji bicubic - tego samego procesu używanego przez Photoshopa (jeśli robisz to dobrze) - i robią to szybciej niż proces canvas. Po prostu określ żądany rozmiar obrazu (użyj tylko jednego wymiaru, wysokości lub szerokości, aby proporcjonalnie zmienić rozmiar).

Jest to obsługiwane przez większość przeglądarek, w tym nowsze wersje IE. Wcześniejsze wersje mogą wymagać specyficznego dla przeglądarki CSS .

Prosta funkcja (za pomocą jQuery) do zmiana rozmiaru obrazu będzie następująca:

function resizeImage(img, percentage) {
    var coeff = percentage/100,
        width = $(img).width(),
        height = $(img).height();

    return {"width": width*coeff, "height": height*coeff}           
}

EDIT zmieniono image na img, aby dopasować args funkcji. ^)^

Następnie użyj zwracanej wartości, aby zmienić rozmiar obrazu w jednym lub obu wymiarach.

Oczywiście są różne ulepszenia, które możesz zrobić, ale to załatwia sprawę.

Dodatek

Wklej poniższy kod do konsoli tej strony i obserwuj, co dzieje się z gravatarami:

function resizeImage(img, percentage) {
    var coeff = percentage/100,
        width = $(img).width(),
        height = $(img).height();

    return {"width": width*coeff, "height": height*coeff}           
}

$('.user-gravatar32 img').each(function(){
  var newDimensions = resizeImage( this, 150);
  this.style.width = newDimensions.width + "px";
  this.style.height = newDimensions.height + "px";
});
 17
Author: Robusto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-02 13:02:42

Jeśli chcesz używać tylko canvas, najlepszy wynik będzie z wieloma downstepami. Ale to jeszcze nie jest dobre enougth. Dla lepszej jakości potrzebujesz czystej implementacji js. Właśnie wypuściliśmy pica - Szybki downscaler o zmiennej jakości / prędkości. Krótko mówiąc, zmienia rozmiar 1280*1024px w ~0.1 s, a obraz 5000*3000px w 1s, z najwyższą jakością (filtr lanczos z 3 płatami). Pica ma demo , w którym możesz grać swoimi obrazami, poziomami jakości, a nawet wypróbować je na urządzeniach mobilnych urządzenia.

Pica nie ma jeszcze maski unsharp, ale zostanie ona dodana wkrótce. Jest to o wiele łatwiejsze niż zaimplementowanie szybkiego filtra splotowego do zmiany rozmiaru.

 16
Author: Vitaly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 11:36:50

Nie jest to właściwa odpowiedź dla osób, które naprawdę potrzebują zmienić rozmiar samego obrazu, ale tylko po to, aby zmniejszyć rozmiar pliku .

Miałem problem ze zdjęciami "bezpośrednio z aparatu", które moi klienci często przesyłali w "nieskompresowanym" formacie JPEG.

Nie jest tak dobrze znany, że canvas obsługuje (w większości przeglądarek 2017), aby zmienić jakość JPEG

data=canvas.toDataURL('image/jpeg', .85) # [1..0] default 0.92

Dzięki tej sztuczce mógłbym zredukować zdjęcia 4k x 3K z >10MB do 1 lub 2MB, na pewno zależy to od Twojego potrzeb.

Zobacz tutaj

 5
Author: halfbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-16 17:55:36

Oto usługa kątowa wielokrotnego użytku dla wysokiej jakości obrazu / płótna zmiany rozmiaru: https://gist.github.com/fisch0920/37bac5e741eaec60e983

[[1]}usługa obsługuje splot lanczos i stopniowe zmniejszanie skalowania. Podejście splotowe jest wyższą jakością kosztem wolniejszego, podczas gdy stopniowe podejście downscalingowe daje dość antyaliasedowe wyniki i jest znacznie szybsze.

Przykładowe użycie:

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
    })
})
 4
Author: fisch2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-16 23:40:26

Jest to ulepszony filtr zmiany rozmiaru Hermite, który wykorzystuje 1 worker, aby okno nie zamarzało.

Https://github.com/calvintwr/Hermite-resize

 4
Author: Calvintwr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-20 08:40:18

Znalazłem rozwiązanie, które nie wymaga bezpośredniego dostępu do danych pikseli i pętli przez nie, aby wykonać próbkowanie w dół. W zależności od rozmiaru obrazu może to być bardzo zasobochłonne i lepiej byłoby użyć wewnętrznych algorytmów przeglądarki.

Funkcja drawImage () wykorzystuje metodę interpolacji liniowej, resamplingu najbliższego sąsiada. To działa dobrze, gdy nie zmieniasz rozmiaru o więcej niż połowę oryginalnego rozmiaru .

Jeśli zapętlisz do tylko Zmień rozmiar maksymalnie o połowę na raz, wyniki byłyby całkiem dobre i znacznie szybsze niż dostęp do danych pikseli.

Ta funkcja zmniejsza się do połowy w czasie, aż do osiągnięcia pożądanego rozmiaru:

  function resize_image( src, dst, type, quality ) {
     var tmp = new Image(),
         canvas, context, cW, cH;

     type = type || 'image/jpeg';
     quality = quality || 0.92;

     cW = src.naturalWidth;
     cH = src.naturalHeight;

     tmp.src = src.src;
     tmp.onload = function() {

        canvas = document.createElement( 'canvas' );

        cW /= 2;
        cH /= 2;

        if ( cW < src.width ) cW = src.width;
        if ( cH < src.height ) cH = src.height;

        canvas.width = cW;
        canvas.height = cH;
        context = canvas.getContext( '2d' );
        context.drawImage( tmp, 0, 0, cW, cH );

        dst.src = canvas.toDataURL( type, quality );

        if ( cW <= src.width || cH <= src.height )
           return;

        tmp.src = dst.src;
     }

  }
  // The images sent as parameters can be in the DOM or be image objects
  resize_image( $( '#original' )[0], $( '#smaller' )[0] );

Podziękowania dla ten post

 3
Author: Jesús Carrera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-27 14:07:41

context.scale(xScale, yScale)

<canvas id="c"></canvas>
<hr/>
<img id="i" />

<script>
var i = document.getElementById('i');

i.onload = function(){
    var width = this.naturalWidth,
        height = this.naturalHeight,
        canvas = document.getElementById('c'),
        ctx = canvas.getContext('2d');

    canvas.width = Math.floor(width / 2);
    canvas.height = Math.floor(height / 2);

    ctx.scale(0.5, 0.5);
    ctx.drawImage(this, 0, 0);
    ctx.rect(0,0,500,500);
    ctx.stroke();

    // restore original 1x1 scale
    ctx.scale(2, 2);
    ctx.rect(0,0,500,500);
    ctx.stroke();
};

i.src = 'https://static.md/b70a511140758c63f07b618da5137b5d.png';
</script>
 -1
Author: moldcraft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-01 08:10:31