Zmiana rozmiaru obrazu w kanwie HTML5

Próbuję stworzyć miniaturkę po stronie klienta przy użyciu javascript i elementu canvas, ale kiedy zmniejszę obraz, wygląda okropnie. Wygląda to tak, jakby został zmniejszony w Photoshopie z resamplingiem ustawionym na "najbliższy sąsiad" zamiast Bicubic. Wiem, że to możliwe, aby to wyglądało dobrze, ponieważ Ta strona może to zrobić dobrze za pomocą płótna, jak również. Próbowałem użyć tego samego kodu, co pokazano w linku " [Source]", ale nadal wygląda okropnie. Na coś mi umyka, jakieś ustawienie, które trzeba ustawić, czy coś?

EDIT:

Próbuję zmienić rozmiar jpg. Próbowałem zmienić rozmiar tego samego jpg na połączonej stronie i w Photoshopie i wygląda dobrze, gdy jest zmniejszony.

Oto odpowiedni kod:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2:

Wydaje się, że się myliłem, linkowana strona nie robiła nic lepszego z pracy zmniejszania rozmiaru obrazu. Próbowałem innych sugerowanych metod i żadna z nich nie wygląda lepiej. To jest to, co różne metody zaowocowały:

Photoshop:

alt text

Canvas:

alt text

Obraz z renderowaniem obrazu: optimizequality ustawiony i skalowany z szerokością / wysokością:

alt text

Obraz z image-rendering: optimizequality set i skalowany z-moz-transform:

alt text

Zmiana rozmiaru płótna na pixastic:

alt text

To chyba oznacza, że firefox nie używa Bicubic samplingu, jak powinien. Muszę tylko poczekać. dopóki go nie dodadzą.

EDIT3:

Oryginalny Obraz

Author: Telanor, 2010-02-20

18 answers

Więc co zrobić, jeśli wszystkie przeglądarki (właściwie Chrome 5 dał mi całkiem dobrą) nie dają wystarczająco dobrej jakości resamplingu? Więc sam je realizujesz! O Daj spokój, wkraczamy w nową erę Web 3.0, przeglądarek zgodnych z HTML5, super zoptymalizowanych kompilatorów JIT javascript, maszyn wielordzeniowych (†), z tonami pamięci, czego się boisz? Hej, jest słowo java w javascript, więc to powinno zagwarantować wydajność, prawda? Oto miniaturka generująca kod:

// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function(x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1;
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width : sx,
        height : Math.round(img.height * sx / img.width),
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(this.process1, 0, this, 0);
}

thumbnailer.prototype.process1 = function(self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
                            + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
};

...dzięki któremu możesz uzyskać takie rezultaty!

img717.imageshack.us/img717/8910/lanczos358.png

Tak czy inaczej, oto 'poprawiona' wersja twojego przykładu:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

Teraz nadszedł czas, aby umieścić swoje najlepsze przeglądarki tam i zobaczyć, która z nich będzie najmniej prawdopodobne zwiększenie ciśnienia krwi twojego klienta!

Umm, gdzie jest mój znacznik sarkazmu?

(ponieważ wiele części kodu opiera się na Anrieff Gallery Generator czy jest on również objęty GPL2? Nie wiem)

w rzeczywistości z powodu ograniczeń javascript, wielordzeniowy nie jest obsługiwany.

 378
Author: syockit,
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-03-17 15:43:30

Szybki algorytm zmiany rozmiaru/ponownego próbkowania obrazu za pomocą filtra Hermite z JavaScript. Wsparcie przejrzystości, daje dobrą jakość. Podgląd:

Tutaj wpisz opis obrazka

Update : Wersja 2.0 została dodana na GitHub (szybsza, web workers + przenoszalne obiekty). W końcu to działa!

Git: https://github.com/viliusle/Hermite-resize
Demo: http://viliusle.github.io/miniPaint/

/**
 * 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);
}
 36
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
2017-09-12 11:50:08

Try pica - to wysoce zoptymalizowany resizer z wybieranymi algorytmami. Zobacz demo .

Na przykład oryginalny obraz z pierwszego postu jest zmieniany w 120ms z filtrem Lanczos i oknem 3px lub 60ms z filtrem pudełkowym i oknem 0,5 px. Dla ogromnego obrazu 17mb 5000x3000px zmiana rozmiaru zajmuje ~1s na komputerze stacjonarnym i 3S na telefonie komórkowym.

Wszystkie zasady zmiany rozmiaru zostały bardzo dobrze opisane w tym wątku, a pica nie dodaje do nich wiedzy. Ale jest zoptymalizowany bardzo dobrze dla nowoczesnych JIT-s i jest gotowy do użycia po wyjęciu z pudełka (przez npm lub bower). Ponadto używa webworkerów, gdy są dostępne, aby uniknąć zawieszania interfejsu.

Planuję również dodać obsługę maski unsharp wkrótce, ponieważ jest to bardzo przydatne po obniżeniu skali.

 21
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:04:00

Wiem, że jest to stary wątek, ale może być przydatny dla niektórych osób takich jak ja, które kilka miesięcy później uderzają w ten problem po raz pierwszy.

Oto kod, który zmienia rozmiar obrazu przy każdym przeładowaniu obrazu. Zdaję sobie sprawę, że nie jest to wcale optymalne, ale podaję to jako dowód koncepcji.

Również, przepraszam za używanie jQuery dla prostych selektorów, ale czuję się zbyt komfortowo ze składnią.

$(document).on('ready', createImage);
$(window).on('resize', createImage);

var createImage = function(){
  var canvas = document.getElementById('myCanvas');
  canvas.width = window.innerWidth || $(window).width();
  canvas.height = window.innerHeight || $(window).height();
  var ctx = canvas.getContext('2d');
  img = new Image();
  img.addEventListener('load', function () {
    ctx.drawImage(this, 0, 0, w, h);
  });
  img.src = 'http://www.ruinvalor.com/Telanor/images/original.jpg';
};
html, body{
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #000;
}
canvas{
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas Resize</title>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

My createImage funkcja jest wywoływana raz podczas ładowania dokumentu, a następnie jest wywoływana za każdym razem, gdy okno otrzyma zdarzenie zmiany rozmiaru.

Testowałem go w Chrome 6 i Firefox 3.6, oba na Macu. Ta "technika" zjada procesor, jak gdyby były lody w lecie, ale robi sztuczkę.
 14
Author: cesarsalazar,
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-08-07 07:07:40

Umieściłem kilka algorytmów do interpolacji obrazu na tablicach pikseli html canvas, które mogą być przydatne tutaj:

Http://jsperf.com/pixel-interpolation/2

Mogą być kopiowane/wklejane i mogą być używane wewnątrz Web workerów do zmiany rozmiaru obrazów(lub innych operacji wymagających interpolacji-używam ich do defishowania obrazów w tej chwili).

Nie dodałem rzeczy lanczos powyżej, więc możesz dodać to jako porównanie, jeśli chcesz.

 9
Author: Daniel,
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
2011-10-26 20:29:10

Jeśli po prostu próbujesz zmienić rozmiar obrazu, polecam ustawienie width i height obrazu za pomocą CSS. Oto krótki przykład:

.small-image {
    width: 100px;
    height: 100px;
}

Zauważ, że height i width można również ustawić za pomocą JavaScript. Oto krótki przykład kodu:

var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you'll confuse IE

Aby mieć pewność, że zmieniany obraz wygląda dobrze, dodaj następujące reguły css do selektora obrazów:

Z tego co wiem, wszystkie przeglądarki oprócz IE używają algorytmu bicubic do zmiany rozmiaru obrazów domyślnie, więc twoje obrazy powinny wyglądać dobrze w Firefoksie i Chrome.

Jeśli ustawienie css width i height nie działa, możesz chcieć pobawić się css transform:

Jeśli z jakiegokolwiek powodu potrzebujesz, aby użyć płótna, pamiętaj, że istnieją dwa sposoby zmiany rozmiaru obrazu: zmieniając rozmiar obszaru roboczego za pomocą css lub rysując obraz w mniejszym rozmiarze.

Zobacz to pytanie aby uzyskać więcej szczegółów.

Mam nadzieję, że to pomoże!

 6
Author: Xavi,
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-05-23 12:02:45

Polecam sprawdzić ten link i upewnić się, że jest ustawiony na true.

Kontrolowanie zachowania skalowania obrazu

Wprowadzony w Gecko 1.9.2 (Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)

Gecko 1.9.2 wprowadził mozImageSmoothingEnabled property to elementu canvas; jeśli ten Boolean wartość jest false, obrazy nie będą wygładzony po skalowaniu. Ta właściwość jest domyślnie true. / align = "left" /

  1. cx.mozImageSmoothingEnabled = false;
 6
Author: Evan Carroll,
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
2010-07-13 17:02:59

Jest to funkcja javascript zaadaptowana z kodu @ Telanor. Przekazując obraz base64 jako pierwszy argument do funkcji, zwraca on base64 zmienionego obrazu. maxWidth i maxHeight są opcjonalne.

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) === 'undefined') var maxWidth = 500;
  if(typeof(maxHeight) === 'undefined') var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}
 6
Author: Christophe Marois,
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-02-13 03:12:02

Do zmiany rozmiaru na obrazek o szerokości mniejszej od oryginału używam:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

I działa=).

 4
Author: Yaffle,
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
2011-03-23 18:27:26

Otrzymałem ten obrazek klikając prawym przyciskiem myszy element canvas w Firefoksie i zapisując jako.

alt text

var img = new Image();
img.onload = function () {
    console.debug(this.width,this.height);
    var canvas = document.createElement('canvas'), ctx;
    canvas.width = 188;
    canvas.height = 150;
    document.body.appendChild(canvas);
    ctx = canvas.getContext('2d');
    ctx.drawImage(img,0,0,188,150);
};
img.src = 'original.jpg';

Tak czy inaczej, oto 'poprawiona' wersja twojego przykładu:

var img = new Image();
// added cause it wasnt defined
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
// adding it to the body

document.body.appendChild(canvasCopy);

var copyContext = canvasCopy.getContext("2d");

img.onload = function()
{
        var ratio = 1;

        // defining cause it wasnt
        var maxWidth = 188,
            maxHeight = 150;

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
};

// changed for example
img.src = 'original.jpg';
 3
Author: robert,
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-11-25 03:24:30

Więc coś ciekawego, co znalazłem jakiś czas temu podczas pracy z canvasem, które może być pomocne:

Aby samodzielnie zmienić rozmiar kontrolki canvas, musisz użyć atrybutów height="" i width="" (lub canvas.width/canvas.height elementów). Jeśli używasz CSS do zmiany rozmiaru płótna, to w rzeczywistości rozciągnie (np.: zmieni rozmiar) Zawartość płótna, aby pasowała do całego płótna (zamiast po prostu zwiększać lub zmniejszać obszar płótna.

Warto spróbować narysować obraz na płótnie steruj przy użyciu atrybutów wysokość i szerokość ustawionych na rozmiar obrazu, a następnie użyj CSS, aby zmienić rozmiar obszaru roboczego do rozmiaru, którego szukasz. Być może użyłoby to innego algorytmu zmiany rozmiaru.

Należy również zauważyć, że canvas ma różne efekty w różnych przeglądarkach (a nawet w różnych wersjach różnych przeglądarek). Algorytmy i techniki stosowane w przeglądarkach prawdopodobnie zmienią się w czasie (zwłaszcza z Firefox 4 i Chrome 6 wychodzi tak szybko, że będzie kłaść duży nacisk na wydajność renderowania canvas).

Ponadto możesz też użyć SVG, ponieważ prawdopodobnie używa on również innego algorytmu.

Powodzenia!
 2
Author: mattbasta,
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
2010-07-06 07:39:53

Problem z niektórymi z tych rozwiązań polega na tym, że uzyskują bezpośredni dostęp do danych pikseli i pętlują je, 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ętlasz, aby zmieniać 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

 2
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:06:26

Mam wrażenie, że moduł, który napisałem, przyniesie podobne wyniki do Photoshopa, ponieważ zachowuje dane kolorów, uśredniając je, a nie stosując algorytm. Jest trochę powolny, ale dla mnie jest najlepszy, ponieważ zachowuje wszystkie dane kolorów.

Https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

Nie pobiera najbliższego sąsiada i nie upuszcza innych pikseli, ani nie pobiera grupy i losowej średniej. Potrzeba dokładnej proporcji każdego piksel źródłowy powinien być wyświetlany w pikselu docelowym. Średni kolor piksela w źródle będzie średnim kolorem piksela w miejscu docelowym, które te inne formuły, myślę, że nie będą.

Przykład użycia znajduje się na dole https://github.com/danschumann/limby-resize

 1
Author: Funkodebat,
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-07-30 20:03:20

Przekonwertowałem odpowiedź @syockit, a także podejście stopniowe do usługi kątowej wielokrotnego użytku dla każdego, kto jest zainteresowany: https://gist.github.com/fisch0920/37bac5e741eaec60e983

Włączyłem oba rozwiązania, ponieważ oba mają swoje plusy / minusy. Podejście Lanczos convolution jest wyższa jakość kosztem bycia wolniejszym, podczas gdy stopniowe podejście downscaling daje dość antyaliased wyniki i jest znacznie szybsze.

Przykład sposób użycia:

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
    })
})
 1
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:39:52

Szybki i prosty JavaScript image resizer:

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

Użycie:

h.resize({
    source: document.getElementById('image'), // any canvas or image elements, jQuery or native
    width: 400,
    height: 600,
    output: 'image', // [optional] `image` or `canvas`. If not entered output is same as input element.
    quality: 0.7, // [optional] applicable for `image` output only
}, function(output) {
    //your callback
});

Historia

To jest naprawdę po wielu rundach badań, czytania i próby.

Algorytm resizer wykorzystuje skrypt Hermite @ViliusL (Hermite resizer jest naprawdę najszybszy i daje dość dobre wyniki). Rozszerzony o funkcje, których potrzebujesz.

Forks 1 worker to do the resizing so that it doesn ' t freeze your browser when zmiana rozmiaru, w przeciwieństwie do wszystkich innych js resizers tam.

 1
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
2015-12-13 07:38:26

Właśnie uruchomiłem stronę porównań side by Side i chyba, że coś się ostatnio zmieniło, nie widziałem lepszego downsizingu (skalowania) przy użyciu canvas vs. simple css. Testowałem w FF6 Mac OSX 10.7. Nadal lekko miękki w stosunku do oryginału.

Natknąłem się jednak na coś, co zrobiło ogromną różnicę i to było używanie filtrów obrazu w przeglądarkach obsługujących canvas. Możesz manipulować obrazami tak jak w Photoshopie za pomocą rozmycia, wyostrzenia, nasycenia, marszczenia, skala szarości itp.

Znalazłem wtedy niesamowitą wtyczkę jQuery, która sprawia, że stosowanie tych filtrów jest bardzo proste: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

Po prostu nakładam filtr wyostrzania zaraz po zmianie rozmiaru obrazu, co powinno dać pożądany efekt. Nie musiałem nawet używać płótna.

 0
Author: Julian Dormon,
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
2011-08-24 19:40:53

Szukasz innego prostego rozwiązania?

var img=document.createElement('img');
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

To rozwiązanie użyje algorytmu zmiany rozmiaru przeglądarki! :)

 0
Author: ale500,
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-20 05:11:57

Dzięki @syockit za rewelacyjną odpowiedź. jednak musiałem sformatować trochę w następujący sposób, aby to działało. Być może ze względu na problemy ze skanowaniem DOM:

$(document).ready(function () {

$('img').on("load", clickA);
function clickA() {
    var img = this;
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 50, 3);
    document.body.appendChild(canvas);
}

function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width: sx,
        height: Math.round(img.height * sx / img.width)
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(process1, 0, this, 0);
}

//returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function (x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    }
}

process1 = function (self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(process1, 0, self, u);
    else
        setTimeout(process2, 0, self);
};

process2 = function (self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
}
});
 0
Author: Manish,
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
2018-02-03 20:35:04