Dapatkan warna gambar rata-rata melalui Javascript

118

Tidak yakin ini mungkin, tetapi ingin menulis skrip yang akan mengembalikan rata-rata hexatau rgbnilai untuk sebuah gambar. Saya tahu ini bisa dilakukan dalam AS tetapi ingin melakukannya dalam JavaScript.

bgreater
sumber
19
teman saya Boaz telah melalui jalan ini sebelumnya (mudah-mudahan dia ikut campur) tetapi rata-rata biasanya membuat Anda dengan warna seperti muntah. Yang Anda inginkan sebenarnya adalah "warna dominan". Ada beberapa cara untuk mendapatkannya (histogram rona w / pengelompokan dinamis dll) tetapi saya pikir itu mungkin lebih tepat untuk hasil yang Anda inginkan.
Paul Irish

Jawaban:

149

AFAIK, satu-satunya cara untuk melakukan ini adalah dengan <canvas/>...

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

Perhatikan, ini hanya akan bekerja dengan gambar di domain yang sama dan di browser yang mendukung kanvas HTML5:

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;

}

Untuk IE, lihat excanvas .

James
sumber
1
Adakah cara untuk mendapatkan histogram warna untuk gambar yang berada di domain berbeda?
Dan Loewenherz
1
Dan: Saya rasa Anda mengalami pembatasan lintas-sumber jika mencoba mengakses data gambar pada gambar dari domain lain. Yang menyedihkan.
8
Saya pikir ada penempatan yang buruk untuk nilai biru dan hijau, Anda menulis 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'dan seharusnya begitu 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. aneh kalau warna dominannya biru tapi hasilnya hijau :).
Ariona Rian
7
Menemukan solusi yang bagus untuk pembatasan lintas sumber: cukup tambahkan img.crossOrigin = '';sebelum menyetel srcatribut. Ditemukan di: coderwall.com/p/pa-2uw
mhu
Tidak perlu menghitung, karena count === length / 4 / blockSize;)
yckart
84

Saya pikir saya akan memposting proyek yang baru-baru ini saya temukan untuk mendapatkan warna dominan:

Pencuri Warna

Skrip untuk mengambil warna dominan atau palet warna representatif dari sebuah gambar. Menggunakan javascript dan kanvas.

Solusi lain yang menyebutkan dan menyarankan warna dominan tidak pernah benar-benar menjawab pertanyaan dalam konteks yang tepat ("dalam javascript"). Semoga proyek ini akan membantu mereka yang ingin melakukan hal itu.

J. Chase
sumber
55

"Warna Dominan" itu rumit. Yang ingin Anda lakukan adalah membandingkan jarak antara setiap piksel dan setiap piksel lainnya dalam ruang warna (Jarak Euclidean), lalu temukan piksel yang warnanya paling dekat dengan setiap warna lainnya. Pixel itu adalah warna dominan. Warna rata-rata biasanya lumpur.

Saya berharap saya memiliki MathML di sini untuk menunjukkan Jarak Euclidean. Google itu.

Saya telah menyelesaikan eksekusi di atas dalam ruang warna RGB menggunakan PHP / GD di sini: https://gist.github.com/cf23f8bddb307ad4abd8

Namun ini sangat mahal secara komputasi. Ini akan merusak sistem Anda pada gambar besar, dan pasti akan merusak browser Anda jika Anda mencobanya di klien. Saya telah mengerjakan pemfaktoran ulang eksekusi saya untuk: - menyimpan hasil dalam tabel pencarian untuk digunakan di masa mendatang dalam iterasi pada setiap piksel. - untuk membagi gambar besar menjadi kotak 20px 20px untuk dominasi lokal. - untuk menggunakan jarak euclidean antara x1y1 dan x1y2 untuk mengetahui jarak antara x1y1 dan x1y3.

Tolong beri tahu saya jika Anda membuat kemajuan di bidang ini. Saya akan senang melihatnya. Aku akan melakukan hal yang sama.

Kanvas jelas merupakan cara terbaik untuk melakukan ini di klien. SVG tidak, SVG berbasis vektor. Setelah saya menyelesaikan eksekusi, hal berikutnya yang ingin saya lakukan adalah menjalankan ini di kanvas (mungkin dengan webworker untuk perhitungan jarak keseluruhan setiap piksel).

Hal lain yang perlu dipikirkan adalah bahwa RGB bukanlah ruang warna yang baik untuk melakukan hal ini, karena jarak euclidean antar warna dalam ruang RGB tidak terlalu dekat dengan jarak visual. Ruang warna yang lebih baik untuk melakukan ini mungkin LUV, tetapi saya belum menemukan perpustakaan yang baik untuk ini, atau algorythims untuk mengubah RGB ke LUV.

Pendekatan yang sepenuhnya berbeda adalah dengan mengurutkan warna Anda dalam pelangi, dan membangun histogram dengan toleransi untuk memperhitungkan berbagai corak warna. Saya belum mencobanya, karena menyortir warna dalam pelangi itu sulit, begitu pula histogram warna. Saya mungkin akan mencoba ini selanjutnya. Sekali lagi, beri tahu saya jika Anda membuat kemajuan di sini.


sumber
6
Ini adalah salah satu jawaban yang saya harap dapat saya lakukan lebih dari sekadar +1 :-)
David Johnstone
3
Jawaban yang sangat baik meskipun solusi yang jelas tidak diberikan. Ini akan membantu saya memutuskan langkah saya selanjutnya ...
baik
Ini adalah jawaban yang bagus. Saya menulis beberapa modul node untuk melakukan penghitungan jarak euclidean bukan di LUV, tetapi di ruang warna L a b *. Lihat github.com/zeke/color-namer dan github.com/zeke/euclidean-distance
Zeke
1
@ Pengguna: Saya hanya ingin tahu: bukankah, katakanlah, 1% sampel cukup pada gambar besar untuk mempercepat komputasi?
jackthehipster
Kedengarannya seperti median geometris. Mungkin ini bisa membantu? stackoverflow.com/questions/12934213/… dan jika poin yang Anda cari harus ada di set asli: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence Weru
16

Pertama: dapat dilakukan tanpa HTML5 Canvas atau SVG.
Sebenarnya, seseorang baru saja berhasil membuat file PNG sisi klien menggunakan JavaScript , tanpa kanvas atau SVG, menggunakan skema URI data .

Kedua: Anda mungkin sebenarnya tidak membutuhkan Canvas, SVG, atau yang di atas sama sekali.
Jika Anda hanya perlu memproses gambar di sisi klien, tanpa mengubahnya, semua ini tidak diperlukan.

Anda bisa mendapatkan alamat sumber dari tag img di halaman, membuat permintaan XHR untuk itu - kemungkinan besar akan datang dari cache browser - dan memprosesnya sebagai aliran byte dari Javascript.
Anda akan membutuhkan pemahaman yang baik tentang format gambar. (Generator di atas sebagian didasarkan pada sumber libpng dan mungkin memberikan titik awal yang baik.)

Andras Vass
sumber
1 untuk solusi bytestream. Ini bagus jika satu-satunya pilihan adalah membuatnya di aplikasi klien.
loretoparisi
11

Saya akan mengatakan melalui tag kanvas HTML.

Anda dapat menemukan di sini posting oleh @Georg berbicara tentang kode kecil oleh pengembang Opera:

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

Ini membalikkan gambar dengan menggunakan nilai R, G dan B dari setiap piksel. Anda dapat dengan mudah menyimpan nilai RGB, lalu mengumpulkan array Merah, Hijau dan Biru, dan akhirnya mengubahnya kembali menjadi kode HEX.

rnaud
sumber
apakah ada kemungkinan ada alternatif IE untuk solusi kanvas?
bgreater
@ Ben4Himv: Ada Pratinjau Platform IE9 ;-) tapi sungguh, sayangnya tidak.
Andy E
4

Javascript tidak memiliki akses ke data warna piksel individu gambar. Setidaknya, mungkin tidak sampai html5 ... pada saat itu masuk akal bahwa Anda akan dapat menggambar gambar ke kanvas, dan kemudian memeriksa kanvas (mungkin, saya belum pernah melakukannya sendiri).

Joel Martinez
sumber
2

Solusi All-In-One

Saya pribadi akan menggabungkan Color Thief bersama dengan versi Modifikasi Nama itu Warna untuk mendapatkan rangkaian hasil warna dominan yang lebih dari cukup untuk gambar.

Contoh:

Perhatikan gambar berikut:

masukkan deskripsi gambar di sini

Anda dapat menggunakan kode berikut untuk mengekstrak data gambar yang berkaitan dengan warna dominan:

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;
Berikan Miller
sumber
1

Ini tentang "Kuantisasi Warna" yang memiliki beberapa pendekatan seperti MMCQ (Kuantisasi Pemotongan Median yang Dimodifikasi) atau OQ (Kuantisasi Oktree). Pendekatan yang berbeda menggunakan K-Means untuk mendapatkan kelompok warna.

Saya telah menggabungkan semuanya di sini, karena saya menemukan solusi untuk di tvOSmana terdapat subset XHTML, yang tidak memiliki <canvas/>elemen:

Hasilkan Warna Dominan untuk gambar RGB dengan XMLHttpRequest

loretoparisi
sumber
1

Cara yang kurang akurat tapi tercepat untuk mendapatkan warna gambar rata-rata dengan datauridukungan:

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);
}
350D
sumber
1

Seperti yang dikemukakan dalam jawaban lain, seringkali yang sebenarnya Anda inginkan adalah warna dominan dibandingkan dengan warna rata-rata yang cenderung cokelat. Saya menulis skrip yang mendapatkan warna paling umum dan mempostingnya di inti ini

dctalbot.dll
sumber
-2

Ada alat online pickimagecolor.com yang membantu Anda menemukan rata-rata atau warna dominan gambar. Anda hanya perlu mengupload gambar dari komputer, lalu mengklik gambar. Ini memberikan warna rata-rata dalam HEX, RGB dan HSV. Ia juga menemukan corak warna yang cocok dengan warna itu untuk dipilih. Saya telah menggunakannya beberapa kali.

shubham agrawal
sumber