HTML5 tuvalindeki bir görüntüyü yeniden boyutlandırma


314

Javascript ve bir tuval öğesi kullanarak istemci tarafında bir küçük resim oluşturmaya çalışıyorum, ancak görüntüyü küçültmek, korkunç görünüyor. Bicubic yerine 'En Yakın Komşu' olarak ayarlanmış yeniden örnekleme ile photoshop'ta küçültülmüş gibi görünüyor. Bunun doğru görünmesini sağlamanın mümkün olduğunu biliyorum, çünkü bu site bir tuval kullanarak da iyi yapabilir. "[Kaynak]" bağlantısında gösterildiği gibi yaptıkları aynı kodu kullanarak denedim, ama yine de korkunç görünüyor. Eksik olduğum, ayarlanması gereken bir ayar veya başka bir şey var mı?

DÜZENLE:

Bir jpg boyutunu değiştirmeye çalışıyorum. Bağlantılı sitede ve photoshop aynı jpg yeniden boyutlandırma denedim ve küçültüldüğünde iyi görünüyor.

İlgili 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:

Yanılmışım gibi görünüyor, bağlantılı web sitesi görüntüyü küçültme işini daha iyi yapmıyordu. Önerilen diğer yöntemleri denedim ve hiçbiri daha iyi görünmüyor. Farklı yöntemlerin ortaya çıkardığı şey budur:

Photoshop:

alternatif metin

Tuval:

alternatif metin

Görüntü işleme özelliğine sahip görüntü: optimizeQuality seti ve genişliği / yüksekliği ile ölçeklendirme:

alternatif metin

Görüntü işleme özellikli görüntü: optimizeMuality seti ve -moz-transform ile ölçeklendirilir:

alternatif metin

Tuval piksel boyutunda yeniden boyutlandır:

alternatif metin

Sanırım bu, firefox'un sözde olduğu gibi bikubik örnekleme kullanmadığı anlamına geliyor. Sadece ekleyene kadar beklemem gerekecek.

EDIT3:

Orijinal görüntü


Görüntüyü yeniden boyutlandırmak için kullandığınız kodu gönderebilir misiniz?
Xavi

Bir GIF görüntüsünü veya benzer bir görüntüyü sınırlı bir paletle yeniden boyutlandırmaya mı çalışıyorsunuz? Photoshop'ta bile, RGB'ye dönüştürmediğiniz sürece bu görüntüler iyi ölçeklenmez.
leepowers

Orijinal görüntünün bir kopyasını gönderebilir misiniz?
Xavi

Javascript kullanarak görüntüyü yeniden boyutlandırmak biraz zorlayıcıdır - sadece görüntüyü yeniden boyutlandırmak için istemci işlem gücünü kullanmakla kalmaz, her sayfa yüklemesinde de yaparsınız. Neden sadece küçültülmüş bir sürümü photoshop'tan kaydetmiyor ve onun yerine / orijinal görüntüyle birlikte sunmuyorsunuz?
tanımlar

29
Çünkü yüklemeden önce görüntüleri yeniden boyutlandırma ve kırpma özelliğine sahip bir resim yükleyici yapıyorum.
Telanor

Yanıtlar:


393

Peki tüm tarayıcılar (aslında, Chrome 5 bana oldukça iyi bir tane verdi) size yeterince iyi örnekleme kalitesi vermezse ne yaparsınız? Onları sen kendin uygula! Oh hadi, Web 3.0'ın yeni çağına, HTML5 uyumlu tarayıcılara, süper optimize JIT javascript derleyicilerine, çok çekirdekli (†) makinelere, tonlarca belleğe giriyoruz, korkuyor musunuz? Hey, javascript'te java kelimesi var, bu yüzden performansı garanti etmeli, değil mi? Bakın, küçük resim üreten kodu:

// 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";
};

... bunun gibi sonuçlar üretebilirsiniz!

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

her neyse, işte örneğin "sabit" bir versiyonu:

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

Şimdi en iyi tarayıcılarınızı oraya çıkarmanın ve hangisinin müşterinizin kan basıncını en az artıracağını görmenin zamanı geldi!

Umm, alaycı etiketim nerede?

(kodun birçok bölümü Anrieff Gallery Generator'a dayalı olduğu için kapsamında da var mı?

aslında javascript sınırlaması nedeniyle, çok çekirdekli desteklenmez.


2
Aslında bunu kendim uygulamaya çalıştım, yaptığınız gibi yapın, açık kaynak kodlu bir resim düzenleyiciden kod kopyalamaya çalıştım. Algoritma hakkında sağlam bir belge bulamadığım için onu optimize etmekte zorlandım. Sonunda, benimki biraz yavaştı (görüntüyü yeniden boyutlandırmak birkaç saniye sürdü). Şansı elde ettiğimde, seninkini deneyeceğim ve daha hızlı olup olmadığını göreceğim. Bence web işçileri artık çok çekirdekli javascript'i mümkün kılıyor. Hızlandırmak için onları kullanmayı deneyecektim, ama bunu çok iş parçacıklı bir algoritmaya nasıl dönüştürdüğümü anlamakta zorlanıyordum
Telanor

3
Üzgünüm, unuttum! Cevabı düzenledim. Zaten hızlı olmayacak, bikübik daha hızlı olmalı. Kullandığım algoritmadan bahsetmiyorum, normal 2 yönlü yeniden boyutlandırma (satır satır, yatay, sonra dikey), bu yüzden bir yavaş yavaş.
syockit

5
Sen harikasın ve tonlarca harikalık hak ediyorsun.
Rocklan

5
Bu iyi sonuçlar verir, ancak Chrome'un son sürümünde 1.8 MP görüntü için 7.4 saniye sürer ...
mpen

2
Böyle yöntemler nasıl bu kadar yüksek puan başa? Gösterilen çözüm, renk bilgisini saklamak için kullanılan logaritmik ölçeği tamamen hesaba katmaz. 127.127.127'lik bir RGB, yarısı değil 255, 255, 255 parlaklığının dörtte biridir. Çözeltideki aşağı örnekleme, karanlık bir görüntüyle sonuçlanır. Utanç, Photoshop'un (OP tercihleri ​​yanlış ayarlanmış olmalıdır) örneğinden daha iyi sonuçlar veren boyut için çok basit ve hızlı bir yöntem olduğu için kapatıldı
Blindman67

37

JavaScript ile Hermite filtresi kullanarak hızlı görüntü yeniden boyutlandırma / yeniden örnekleme algoritması . Şeffaflığı destekleyin, iyi kalite verin. Ön izleme:

resim açıklamasını buraya girin

Güncelleme : GitHub'a sürüm 2.0 eklendi (daha hızlı, web çalışanları + aktarılabilir nesneler). Sonunda işe aldım!

Git: https://github.com/viliusle/Hermite-resize
Gösteri: 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);
}

Belki miniPaint demonuz ve Github repo bağlantınızı ekleyebilirsiniz?
syockit

1
Webworkers sürümünü de paylaşacak mısınız? Muhtemelen kurulum yükü nedeniyle, küçük görüntüler için daha yavaştır, ancak daha büyük kaynak görüntüler için yararlı olabilir.
syockit

demo eklendi, git bağlantılar, ayrıca çok çekirdekli sürümü. BTW çok çekirdekli sürümü optimize etmek için çok fazla zaman harcamak yoktu ... Tek sürüm iyi optimize olduğuna inanıyorum.
ViliusL

Büyük fark ve iyi performans. Çok teşekkür ederim! önce ve sonra
KevBurnsJr

2
@ViliusL Ah şimdi web çalışanlarının neden bu kadar iyi çalışmadığını hatırladım. Daha önce hafızayı paylaşmamışlardı ve hala sahip değiller! Belki bir gün bunu
çözmeyi başardıklarında

26

Deneyin pika seçilebilir Algorythms ile oldukça optimize resizer var -. Demoya bakınız .

Örneğin, ilk yazının orijinal görüntüsü Lanczos filtresi ve 3px penceresi ile 120ms veya Kutu filtresi ve 0.5px penceresi ile 60ms olarak yeniden boyutlandırılır. Büyük 17mb görüntü için 5000x3000px yeniden boyutlandırma masaüstünde ~ 1s ve mobilde 3s alır.

Tüm yeniden boyutlandırma ilkeleri bu iş parçacığında çok iyi açıklanmıştır ve pika roket bilimi eklemez. Ancak modern JIT-ler için çok iyi optimize edilmiştir ve kullanıma hazırdır (npm veya bower üzerinden). Ayrıca, arayüz donmalarını önlemek için kullanılabilir olduğunda web işçileri kullanır.

Ayrıca kısa bir süre sonra keskin olmayan maske desteği eklemeyi planlıyorum, çünkü küçülmeden sonra çok yararlı.


14

Bu eski bir iş parçacığı olduğunu biliyorum ama aylar sonra bu konuyu ilk kez vurmak gibi benim gibi bazı insanlar için yararlı olabilir.

Görüntüyü her yüklediğinizde görüntüyü yeniden boyutlandıran bazı kodlar aşağıdadır. Bunun hiç de optimal olmadığının farkındayım, ama bunu bir kavram kanıtı olarak sağlıyorum.

Ayrıca, basit seçiciler için jQuery kullandığım için üzgünüm ama ben sadece sözdizimi ile çok rahat hissediyorum.

$(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>

CreateImage işlevim belge yüklendiğinde bir kez çağrılır ve bundan sonra pencere her yeniden boyutlandırma olayı aldığında çağrılır.

Her ikisini de Mac'te Chrome 6 ve Firefox 3.6'da test ettim. Bu "teknik" yaz aylarında dondurma gibi işlemciyi yiyor, ama hile yapıyor.


9

Burada yararlı olabilecek html tuval piksel dizilerinde görüntü enterpolasyonu yapmak için bazı algoritmalar koydum:

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

Bunlar kopyalanabilir / yapıştırılabilir ve görüntüleri yeniden boyutlandırmak için web çalışanlarının içinde kullanılabilir (veya enterpolasyon gerektiren başka bir işlem - şu anda görüntüleri defish etmek için kullanıyorum).

Yukarıdaki lanczos öğelerini eklemedim, bu yüzden isterseniz bir karşılaştırma olarak eklemekten çekinmeyin.


6

Bu @ Telanor kodundan uyarlanmış bir javascript işlevidir. Bir işlev base64 işlevine ilk bağımsız değişken olarak iletilirken, yeniden boyutlandırılan görüntünün base64 öğesini döndürür. maxWidth ve maxHeight isteğe bağlıdır.

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

}

yaklaşımınız çok hızlıdır ama burada gördüğünüz gibi bir bulanık görüntü üretir: stackoverflow.com/questions/18922880/...
yapılandırma dosyasındaki

6

Bu bağlantıyı kontrol etmenizi ve doğru olarak ayarlandığından emin olmanızı şiddetle tavsiye ederim .

Görüntü ölçekleme davranışını kontrol etme

Gecko 1.9.2'de tanıtıldı (Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)

Gecko 1.9.2, canvas öğesine mozImageSmoothingEnabled özelliğini tanıttı; bu Boolean değeri false olursa, ölçeklendiğinde görüntüler düzgünleştirilmez. Bu özellik varsayılan olarak doğrudur. düzyazıyı görüntüle?

  1. cx.mozImageSmoothingEnabled = yanlış;

5

Sadece bir görüntüyü yeniden çalışıyorsanız, ben ayarı öneriyoruz widthve heightCSS ile resmin. İşte kısa bir örnek:

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

heightVe widthöğesinin JavaScript kullanılarak da ayarlanabileceğini unutmayın . İşte hızlı kod örneği:

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

Ayrıca, yeniden boyutlandırılan görüntünün iyi göründüğünden emin olmak için görüntü seçiciye aşağıdaki css kurallarını ekleyin:

Anlayabildiğim kadarıyla, görüntüleri varsayılan olarak yeniden boyutlandırmak için bir bikubik algoritma kullanan IE dışındaki tüm tarayıcılar, bu yüzden yeniden boyutlandırılmış resimlerinizin Firefox ve Chrome'da iyi görünmesi gerekir.

Css ayarı yapılırsa widthve heightçalışmazsa, bir css ile oynamak isteyebilirsiniz transform:

Herhangi bir nedenden dolayı bir tuval kullanmanız gerekiyorsa , lütfen bir görüntünün yeniden boyutlandırılmasının iki yolu olduğunu unutmayın: tuvali css ile yeniden boyutlandırarak veya daha küçük bir boyutta çizerek.

Daha fazla ayrıntı için bu soruya bakın.


5
Ne tuvalin yeniden boyutlandırılması ne de görüntünün daha küçük boyutta çizilmesi sorunu (Chrome'da) ne yazık ki çözmez.
Nestor

1
Chrome 27 hoş bir şekilde yeniden boyutlandırılmış resim üretir, ancak sonucu bir tuval üzerine kopyalayamazsınız; bunu yapmaya çalışmak orijinal görüntüyü kopyalar.
syockit

4

Orijinalinden daha az genişliğe sahip görüntüyü yeniden boyutlandırmak için şunu kullanırım:

    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, .... )

ve çalışır =).


4

Firefox'ta tuval öğesini sağ tıklayıp kaydederek bu görüntüyü aldım.

alternatif metin

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

her neyse, işte örneğin "sabit" bir versiyonu:

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

Yaptığın şeyi yapmayı denedim ve seninki gibi hoş görünmüyor. Bir şeyi kaçırmadıkça, yaptığınız tek değişiklik dilimleme yöntemi yerine ölçekleme yöntemi imzasını kullanmaktı, değil mi? Nedense benim için çalışmıyor.
Telanor

3

Bu çözümlerin bazılarıyla ilgili sorun, doğrudan piksel verilerine erişmeleri ve altörneklemeyi gerçekleştirmek için bu döngüler arasında geçiş yapmalarıdır. Görüntünün boyutuna bağlı olarak bu çok kaynak yoğun olabilir ve tarayıcının dahili algoritmalarını kullanmak daha iyi olur.

DrawImage () işlevi, bir doğrusal interpolasyon kullanarak, en yakın komşu yöntemi yeniden örnekleme. Orijinal boyutun yarısından fazlasını küçültmediğinizde bu işe yarar .

Bir kerede en fazla bir buçuk boyutu yeniden boyutlandırmak istiyorsanız, sonuçlar oldukça iyi ve piksel verilerine erişmekten çok daha hızlı olur.

Bu işlev, istenen boyuta ulaşana kadar bir seferde yarıya indirilir:

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

Bu gönderiye verilen krediler


2

Tuval ile çalışırken bir süre önce bulduğum ilginç bir şey yardımcı olabilir:

Tuval denetimini tek başına yeniden boyutlandırmak için height=""ve width=""niteliklerini (veya canvas.width/canvas.height öğelerini) . Tuvali yeniden boyutlandırmak için CSS kullanırsanız, tuvalin içeriğini tuvalin alanını genişletmek veya azaltmak yerine tam tuvale uyacak şekilde genişletir (yani: yeniden boyutlandırır).

Görüntüyü, boy ve genişlik nitelikleri görüntünün boyutuna ayarlanmış bir tuval denetimine çizmeyi ve ardından tuvali aradığınız boyuta göre yeniden boyutlandırmak için CSS'yi kullanmayı denemeye değer. Belki de bu farklı bir yeniden boyutlandırma algoritması kullanır.

Tuvalin farklı tarayıcılarda (ve hatta farklı tarayıcıların farklı sürümlerinde) farklı etkileri olduğu da not edilmelidir. Tarayıcılarda kullanılan algoritmaların ve tekniklerin zaman içinde değişmesi muhtemeldir (özellikle Firefox 4 ve Chrome 6 çok yakında çıkacak ve tuval oluşturma performansına büyük önem verilecektir).

Buna ek olarak, muhtemelen farklı bir algoritma kullandığı için SVG'ye de bir şans vermek isteyebilirsiniz.

İyi şanslar!


1
Bir tuvalin genişliğini veya yüksekliğini HTML öznitelikleri aracılığıyla ayarlamak, tuvalin temizlenmesine neden olur, bu nedenle bu yöntemle yeniden boyutlandırma yapılamaz. Ayrıca, SVG matematiksel görüntülerle ilgilenmek içindir. PNG'leri ve benzerlerini çizebilmem gerekiyor, böylece bana orada yardımcı olmayacak.
Telanor

Tuvalin yüksekliğini ve genişliğini ayarlamak ve CSS kullanarak yeniden boyutlandırmak yardımcı olmaz, (Chrome'da) buldum. CSS genişliği / yüksekliği yerine -webkit-transform kullanarak yeniden boyutlandırma yapmak bile enterpolasyonun gerçekleşmesini sağlamaz.
Nestor

2

Yazdığım modülün, algoritma uygulamadan renk verilerini ortalayarak koruduğu için photoshop'a benzer sonuçlar üreteceğini hissediyorum. Bu biraz yavaş, ama benim için en iyisi, çünkü tüm renk verilerini koruyor.

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

En yakın komşuyu almaz ve diğer pikselleri düşürmez veya bir grubu örneklemez ve rastgele bir ortalama almaz. Her kaynak pikselin hedef piksele çıkması gereken tam oranı alır. Kaynaktaki ortalama piksel rengi, hedefteki ortalama piksel rengi olacak, bu diğer formüllerin olmayacağını düşünüyorum.

nasıl kullanılacağına dair bir örnek https://github.com/danschumann/limby-resize adresinin altındadır

GÜNCELLEME EKİM 2018 : Bugünlerde örneğim her şeyden daha akademik. Webgl neredeyse% 100'dür, bu nedenle benzer sonuçlar üretmek için bununla yeniden boyutlandırmanız daha iyi olur, ancak daha hızlıdır. PICA.js bunu yapıyor, inanıyorum. -


1

@ Syockit'in cevabını ve geri çekilme yaklaşımını, ilgilenen herkes için yeniden kullanılabilir bir Açısal hizmete dönüştürdüm: https://gist.github.com/fisch0920/37bac5e741eaec60e983

Her iki çözümü de dahil ettim çünkü ikisinin de kendi artıları / eksileri var. Lanczos evrişim yaklaşımı, yavaş olma pahasına daha yüksek kalitedir, buna karşın kademeli ölçek küçültme yaklaşımı makul ölçüde yumuşatılmış sonuçlar üretir ve önemli ölçüde daha hızlıdır.

Örnek kullanım:

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

Hızlı ve basit Javascript resim boyutlandırıcı:

https://github.com/calvintwr/blitz-hermite-resize

const blitz = Blitz.create()

/* Promise */
blitz({
    source: DOM Image/DOM Canvas/jQuery/DataURL/File,
    width: 400,
    height: 600
}).then(output => {
    // handle output
})catch(error => {
    // handle error
})

/* Await */
let resized = await blizt({...})

/* Old school callback */
const blitz = Blitz.create('callback')
blitz({...}, function(output) {
    // run your callback.
})

Tarih

Bu gerçekten birçok araştırma, okuma ve denemeden sonra.

Yeniden boyutlandırma algoritması @ ViliusL'in Hermite komut dosyasını kullanır (Hermite yeniden boyutlandırıcı gerçekten en hızlıdır ve oldukça iyi çıktı verir). İhtiyacınız olan özelliklerle genişletilmiş.

1 işçinin yeniden boyutlandırmasını yapmasına izin verir, böylece diğer tüm JS yeniden boyutlandırıcılarının aksine yeniden boyutlandırırken tarayıcınızı dondurmaz.


0

Harika bir cevap için @syockit teşekkürler. ancak, çalışması için aşağıdaki gibi biraz yeniden biçimlendirmek zorunda kaldım. Belki de DOM tarama sorunları nedeniyle:

$(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";
}
});

-1

Sadece yan yana karşılaştırmalar sayfasını çalıştırdım ve yakın zamanda bir şey değişmedikçe, tuvalle basit css kullanarak daha iyi küçülme (ölçeklendirme) göremedim. FF6 Mac OSX 10.7'de test ettim. Orijinal ile karşılaştırıldığında hala biraz yumuşak.

Bununla birlikte, büyük bir fark yaratan ve tuvali destekleyen tarayıcılarda görüntü filtreleri kullanan bir şeye rastladım. Photoshop'taki görüntüleri bulanıklaştırma, keskinleştirme, doygunluk, dalgalanma, gri tonlama vb. İle gerçekten değiştirebilirsiniz.

Daha sonra bu filtrelerin uygulanmasını bir çırpıda yapan harika bir jQuery eklentisi buldum: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

İstediğiniz efekti vermesi için görüntüyü yeniden boyutlandırdıktan hemen sonra keskinleştirme filtresini uygularım. Bir tuval elemanı kullanmak zorunda bile değildim.


-1

Başka bir basit ve basit çözüm mü arıyorsunuz?

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

Bu çözüm tarayıcının yeniden boyutlandırma algoritmasını kullanacaktır! :)


Soru, sadece yeniden boyutlandırmakla kalmayıp, görüntüyü küçültmekle ilgilidir.
Jesús Carrera

[...] Bir jpg boyutunu değiştirmeye çalışıyorum. Bağlantılı sitede ve photoshop aynı jpg yeniden boyutlandırmayı denedim ve küçültüldüğünde iyi görünüyor. [...] neden <img> Jesus Carrera kullanamazsınız?
ale500
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.