Javascript ile ortalama görüntü rengini alın


118

Bunun mümkün olduğundan emin değilim, ancak bir görüntünün ortalamasını hexveya rgbdeğerini döndürecek bir komut dosyası yazmak istiyor . AS'de yapılabileceğini biliyorum ama bunu JavaScript'te yapmak istiyorum.


19
arkadaşım Boaz daha önce bu yoldan geçmişti (umarım içeri girer) ama ortalama tipik olarak sizi kusmuk benzeri bir renkle bırakır. Gerçekten istediğin şey "baskın renk". Bunu elde etmenin birkaç yolu var (ton histogramı, dinamik gruplama vb.), Ancak bence bu muhtemelen istediğiniz sonuç için daha kesin.
Paul Irish

Yanıtlar:


149

AFAIK, bunu yapmanın tek yolu <canvas/>...

DEMO V2 : http://jsfiddle.net/xLF38/818/

Bunun yalnızca aynı etki alanındaki resimlerde ve HTML5 tuvalini destekleyen tarayıcılarda çalışacağını unutmayın:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
        defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
        canvas = document.createElement('canvas'),
        context = canvas.getContext && canvas.getContext('2d'),
        data, width, height,
        i = -4,
        length,
        rgb = {r:0,g:0,b:0},
        count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return defaultRGB;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

IE için excanvas'a bakın .


1
Farklı bir alanda bulunan bir görüntünün renk histogramını almanın bir yolu var mı?
Dan Loewenherz

1
Dan: Başka bir etki alanından bir görüntüdeki görüntü verilerine erişmeye çalışırsanız çapraz kaynak kısıtlamasıyla karşılaştığınızı düşünüyorum. Hangi bir serseri.

8
bence mavi ve yeşil değer için endişe verici bir yerleşim var, yazıyorsun 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'ve olmalı 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. baskın renk mavi ama sonuç yeşil olduğunda garip :).
Ariona Rian

7
Çapraz kaynak kısıtlamaları için harika bir çözüm bulundu: sadece özelliği img.crossOrigin = '';ayarlamadan önce ekleyin src. Bulunan: coderwall.com/p/pa-2uw
mhu

count === length / 4 / blockSize
Saymaya

84

Son zamanlarda baskın renk elde etmek için karşılaştığım bir projeyi yayınlayacağımı düşündüm:

Renk Hırsızı

Bir görüntüden baskın rengi veya temsili bir renk paletini almak için bir komut dosyası. Javascript ve canvas kullanır.

Baskın renkten bahseden ve öneren diğer çözümler soruyu hiçbir zaman doğru bağlamda ("javascript'te") yanıtlamaz. Umarım bu proje tam da bunu yapmak isteyenlere yardımcı olur.


55

"Dominant Color" aldatıcıdır. Yapmak istediğiniz, renk uzayında (Öklid Mesafesi) her piksel ile diğer pikseller arasındaki mesafeyi karşılaştırmak ve ardından rengi diğer her renge en yakın olan pikseli bulmaktır. Bu piksel baskın renktir. Ortalama renk genellikle çamur olacaktır.

Keşke burada size Öklid Mesafesini göstermek için MathML olsaydı. Google'da Ara.

RGB renk alanında yukarıdaki uygulamayı PHP / GD kullanarak burada gerçekleştirdim: https://gist.github.com/cf23f8bddb307ad4abd8

Ancak bu, hesaplama açısından çok pahalıdır. Sisteminizi büyük görüntülerde çökertecek ve istemcide denerseniz kesinlikle tarayıcınızı çökertecektir. Yürütmemi şu amaçlarla yeniden düzenleme üzerinde çalışıyorum: - sonuçları ileride her piksel üzerindeki yinelemede kullanmak üzere bir arama tablosunda depolamak. - yerelleştirilmiş hakimiyet için büyük resimleri 20px 20px'lik ızgaralara bölmek. - x1y1 ve x1y3 arasındaki mesafeyi bulmak için x1y1 ve x1y2 arasındaki öklid mesafesini kullanmak.

Bu cephede ilerleme kaydederseniz lütfen bana bildirin. Bunu görmekten mutlu olurum. Aynısını yapacağım.

Tuval, bunu istemcide yapmanın kesinlikle en iyi yoludur. SVG değildir, SVG vektör tabanlıdır. Uygulamayı indirdikten sonra, yapmak istediğim bir sonraki şey bunu tuvalde çalıştırmaktır (belki her pikselin genel mesafe hesaplaması için bir web çalışanıyla).

Düşünülmesi gereken bir diğer konu da RGB'nin bunu yapmak için iyi bir renk alanı olmadığıdır, çünkü RGB alanındaki renkler arasındaki öklid mesafesi görsel mesafeye çok yakın değildir. Bunu yapmak için daha iyi bir renk alanı LUV olabilir, ancak bunun için iyi bir kitaplık veya RGB'yi LUV'ye dönüştürmek için herhangi bir algoritma bulamadım.

Tamamen farklı bir yaklaşım, renklerinizi bir gökkuşağında sıralamak ve bir rengin değişen tonlarını hesaba katmak için toleranslı bir histogram oluşturmak olacaktır. Bunu denemedim, çünkü bir gökkuşağındaki renkleri sıralamak ve renk histogramları da zor. Bunu daha sonra deneyebilirim. Burada ilerleme kaydederseniz yine bize bildirin.


6
Bu, +1 'den fazlasını yapabilmeyi dilediğim cevaplardan biri :-)
David Johnstone

3
Net bir çözüm oluşturulmamış olsa bile mükemmel yanıt. Bu ... benim sonraki adımı karar yardımcı olacaktır
bgreater

Bu harika bir cevap. Öklid mesafe hesaplaması yapmak için LUV'de değil, L a b * renk uzayında birkaç düğüm modülü yazdım . Bkz. Github.com/zeke/color-namer ve github.com/zeke/euclidean-distance
Zeke

1
@user: Sadece merak ediyordum: Büyük görüntülerde hesaplamayı hızlandırmak için% 1 örnek yeterli olmaz mıydı?
jackthehipster

Geometrik medyan gibi geliyor. Belki bu yardımcı olabilir? stackoverflow.com/questions/12934213/… ve aradığınız noktanın orijinal sette bulunması gerekiyorsa : en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence Weru

16

İlk olarak: HTML5 Canvas veya SVG olmadan yapılabilir.
Aslında, birisi veri URI şemasını kullanarak JavaScript kullanarak , tuval veya SVG olmadan istemci tarafı PNG dosyaları oluşturmayı başardı .

İkincisi: Aslında Canvas, SVG veya yukarıdakilerden herhangi birine ihtiyacınız olmayabilir.
Yalnızca gerekirse işlem , istemci tarafında görüntüleri olmadan bunları değiştirerek, tüm bu gerekli değildir.

Kaynak adresini sayfadaki img etiketinden alabilir, bunun için bir XHR isteğinde bulunabilirsiniz - büyük olasılıkla tarayıcı önbelleğinden gelecektir - ve Javascript'ten bir bayt akışı olarak işleyebilirsiniz.
Görüntü formatını iyi anlamanız gerekecektir. (Yukarıdaki oluşturucu kısmen libpng kaynaklarına dayalıdır ve iyi bir başlangıç ​​noktası sağlayabilir.)


Bytestream çözümü için +1. Bu, tek seçenek istemci uygulamasında yapmaksa iyi bir seçenektir.
loretoparisi

11

HTML canvas etiketi ile söyleyebilirim.

Burada @Georg tarafından Opera dev'in küçük bir kodundan bahseden bir yazı bulabilirsiniz :

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = 255 - pix[i  ]; // red
    pix[i+1] = 255 - pix[i+1]; // green
    pix[i+2] = 255 - pix[i+2]; // blue
    // i+3 is alpha (the fourth element)
}

// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);

Bu, her pikselin R, G ve B değerini kullanarak görüntüyü ters çevirir. RGB değerlerini kolayca saklayabilir, ardından Kırmızı, Yeşil ve Mavi dizilerini yuvarlayabilir ve son olarak bunları bir HEX koduna dönüştürebilirsiniz.


tuval çözümüne IE alternatifi olma ihtimali var mı?
bgreater

@ Ben4Himv: IE9 Platform Önizlemesi var ;-) ama cidden, korkarım hayır.
Andy E


4

Javascript, bir görüntünün bireysel piksel rengi verilerine erişemez. En azından, belki html5'e kadar değil ... bu noktada bir tuvale bir resim çizip ardından tuvali inceleyebileceksiniz (belki, ben hiç yapmadım).


2

Hepsi Bir Arada Çözüm

Resimler için fazlasıyla yeterli sayıda baskın renk sonucu elde etmek için Color Thief'i Name that Color'ın bu değiştirilmiş versiyonuyla kişisel olarak birleştirirdim .

Misal:

Aşağıdaki resmi düşünün:

görüntü açıklamasını buraya girin

Baskın renkle ilgili görüntü verilerini çıkarmak için aşağıdaki kodu kullanabilirsiniz:

let color_thief = new ColorThief();
let sample_image = new Image();

sample_image.onload = () => {
  let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
    const hex = x.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
    
  }).join(''));
  
  console.log(result[0]); // #f0c420     : Dominant HEX/RGB value of closest match
  console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
  console.log(result[2]); // #ffff00     : Dominant HEX/RGB value of shade of closest match
  console.log(result[3]); // Yellow      : Dominant color name of shade of closest match
  console.log(result[4]); // false       : True if exact color match
};

sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;


1

datauriDestekle görüntünün ortalama rengini elde etmenin daha az doğru ancak en hızlı yolu :

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}

1

Diğer yanıtlarda da belirtildiği gibi, genellikle kahverengi olma eğiliminde olan ortalama rengin aksine baskın rengi gerçekten istediğiniz şeydir. En yaygın rengi alan bir senaryo yazdım ve bu ana hat üzerinde yayınladım


-2

Resmin ortalamasını veya baskın rengini bulmanıza yardımcı olan çevrimiçi bir araç pickimagecolor.com vardır. Bilgisayarınızdan bir resim yüklemeniz ve ardından resmin üzerine tıklamanız yeterlidir. HEX, RGB ve HSV'de ortalama rengi verir. Ayrıca, aralarından seçim yapabileceğiniz o renge uyan renk tonlarını da bulur. Birden çok kez kullandım.

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.