Ottieni il colore medio dell'immagine tramite Javascript


118

Non sono sicuro che sia possibile, ma sto cercando di scrivere uno script che restituisca la media hexo il rgbvalore per un'immagine. So che può essere fatto in AS ma sto cercando di farlo in JavaScript.


19
il mio amico Boaz ha già intrapreso questa strada (si spera che intervenga) ma normalmente ti lascia con un colore simile al vomito. Quello che vuoi veramente è "colore dominante". Ci sono alcuni modi per ottenerlo (istogramma della tonalità con bucket dinamico, ecc.) Ma penso che probabilmente sia più preciso per il risultato desiderato.
Paul Irish

Risposte:


149

AFAIK, l'unico modo per farlo è con <canvas/>...

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

Nota, questo funzionerà solo con le immagini sullo stesso dominio e nei browser che supportano HTML5 canvas:

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;

}

Per IE, controlla excanvas .


1
Esiste un modo per ottenere l'istogramma del colore per un'immagine situata su un dominio diverso?
Dan Loewenherz

1
Dan: Penso che incappi nella restrizione cross-origin se provi ad accedere ai dati dell'immagine su un'immagine da un altro dominio. Che è un peccato.

8
Penso che ci sia un posizionamento fastidioso per il valore blu e verde, scrivi 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'e dovrebbe essere 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. è strano quando il colore dominante è il blu ma il risultato è il verde :).
Ariona Rian

7
Trovata un'ottima soluzione per le restrizioni cross-origin: basta aggiungere img.crossOrigin = '';prima di impostare l' srcattributo. Trovato su: coderwall.com/p/pa-2uw
mhu

Non c'è bisogno di contare, dal momento che count === length / 4 / blockSize;)
yckart

84

Ho pensato di pubblicare un progetto che ho incontrato di recente per ottenere un colore dominante:

Ladro di colore

Un copione per catturare il colore dominante o una tavolozza di colori rappresentativa da un'immagine. Utilizza javascript e canvas.

Le altre soluzioni che menzionano e suggeriscono il colore dominante non rispondono mai veramente alla domanda nel giusto contesto ("in javascript"). Speriamo che questo progetto aiuti coloro che vogliono fare proprio questo.


55

"Dominant Color" è complicato. Quello che vuoi fare è confrontare la distanza tra ogni pixel e ogni altro pixel nello spazio colore (distanza euclidea), quindi trovare il pixel il cui colore è più vicino a ogni altro colore. Quel pixel è il colore dominante. Il colore medio sarà solitamente fango.

Vorrei avere qui il MathML per mostrarvi la distanza euclidea. Google.

Ho completato l'esecuzione di cui sopra nello spazio colore RGB utilizzando PHP / GD qui: https://gist.github.com/cf23f8bddb307ad4abd8

Questo tuttavia è molto costoso dal punto di vista computazionale. Si bloccherà il tuo sistema su immagini di grandi dimensioni e sicuramente bloccherà il browser se lo provi nel client. Ho lavorato al refactoring della mia esecuzione per: - memorizzare i risultati in una tabella di ricerca per un uso futuro nell'iterazione su ogni pixel. - per dividere immagini di grandi dimensioni in griglie di 20px 20px per dominanza localizzata. - utilizzare la distanza euclidea tra x1y1 e x1y2 per calcolare la distanza tra x1y1 e x1y3.

Per favore fatemi sapere se fate progressi su questo fronte. Sarei felice di vederlo. Farò lo stesso.

La tela è sicuramente il modo migliore per farlo nel client. SVG non lo è, SVG è basato su vettori. Dopo aver interrotto l'esecuzione, la prossima cosa che voglio fare è farlo funzionare nell'area di disegno (magari con un webworker per il calcolo della distanza complessiva di ogni pixel).

Un'altra cosa a cui pensare è che RGB non è un buon spazio colore per fare questo, perché la distanza euclidea tra i colori nello spazio RGB non è molto vicina alla distanza visiva. Uno spazio colore migliore per fare questo potrebbe essere LUV, ma non ho trovato una buona libreria per questo, o alcun algoritmo per convertire RGB in LUV.

Un approccio completamente diverso sarebbe ordinare i colori in un arcobaleno e costruire un istogramma con tolleranza per tenere conto delle diverse sfumature di un colore. Non l'ho provato, perché ordinare i colori in un arcobaleno è difficile, così come gli istogrammi dei colori. Potrei provare questo dopo. Di nuovo, fammi sapere se fai progressi qui.


6
Questa è una di quelle risposte per le quali vorrei poter fare più di un semplice +1 :-)
David Johnstone

3
Ottima risposta anche se non è stata resa chiara una soluzione. Questo mi aiuterà a decidere il mio prossimo passo ...
maggiore del

Questa è un'ottima risposta. Ho scritto un modulo di alcuni nodi per eseguire il calcolo della distanza euclidea non in LUV, ma nello spazio colore L a b *. Vedi github.com/zeke/color-namer e github.com/zeke/euclidean-distance
Zeke

1
@user: Mi stavo solo chiedendo: un campione dell'1% non sarebbe sufficiente su immagini di grandi dimensioni per accelerare il calcolo?
jackthehipster

Sembra la mediana geometrica. Forse questo può aiutare? stackoverflow.com/questions/12934213/… e se il punto che stai cercando deve esistere nel set originale: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence Weru

16

Primo: può essere fatto senza HTML5 Canvas o SVG.
In realtà, qualcuno è appena riuscito a generare file PNG lato client utilizzando JavaScript , senza canvas o SVG, utilizzando lo schema URI dei dati .

Secondo: potresti non aver bisogno di Canvas, SVG o nessuno dei precedenti.
Se hai solo bisogno di elaborare le immagini sul lato client, senza modificarle, tutto ciò non è necessario.

Puoi ottenere l'indirizzo sorgente dal tag img sulla pagina, fare una richiesta XHR per esso - molto probabilmente proverrà dalla cache del browser - ed elaborarlo come flusso di byte da Javascript.
Avrai bisogno di una buona conoscenza del formato dell'immagine. (Il generatore di cui sopra è parzialmente basato su sorgenti libpng e potrebbe fornire un buon punto di partenza.)


+1 per la soluzione bytestream. Questa è una buona opzione se l'unica opzione è quella di farlo sull'applicazione client.
loretoparisi

11

Direi tramite il tag HTML canvas.

Puoi trovare qui un post di @Georg che parla di un piccolo codice dello sviluppatore di 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);

Questo inverte l'immagine usando il valore R, G e B di ogni pixel. È possibile memorizzare facilmente i valori RGB, quindi arrotondare gli array rosso, verde e blu e infine riconvertirli in un codice HEX.


c'è qualche possibilità che esista un'alternativa IE alla soluzione canvas?
bgior

@ Ben4Himv: c'è l'anteprima della piattaforma IE9 ;-) ma sul serio, temo di no.
Andy E


4

Javascript non ha accesso ai dati di colore dei singoli pixel di un'immagine. Almeno, non forse fino a html5 ... a quel punto è logico che sarai in grado di disegnare un'immagine su una tela e poi ispezionare la tela (forse, non l'ho mai fatto io stesso).


2

Soluzione all-in-one

Combinerei personalmente Color Thief insieme a questa versione modificata di Name that Color per ottenere una gamma più che sufficiente di risultati cromatici dominanti per le immagini.

Esempio:

Considera la seguente immagine:

inserisci qui la descrizione dell'immagine

È possibile utilizzare il seguente codice per estrarre i dati dell'immagine relativi al colore dominante:

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

Modo meno accurato ma più veloce per ottenere il colore medio dell'immagine con dataurisupporto:

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

Come sottolineato in altre risposte, spesso quello che vuoi veramente è il colore dominante in contrapposizione al colore medio che tende ad essere il marrone. Ho scritto una sceneggiatura che ottiene il colore più comune e l'ho pubblicata su questa sintesi


-2

C'è uno strumento online pickimagecolor.com che ti aiuta a trovare il colore medio o dominante dell'immagine. Devi solo caricare un'immagine dal tuo computer e quindi fare clic sull'immagine. Fornisce il colore medio in HEX, RGB e HSV. Trova anche le sfumature di colore corrispondenti a quel colore tra cui scegliere. L'ho usato più volte.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.