lokesh / color-thief

Grab the color palette from an image using just Javascript. Works in the browser and in Node.
https://lokeshdhakar.com/projects/color-thief/
MIT License
12.46k stars 1.31k forks source link

Wrong dominant color is chosen #92

Open marcelpi opened 8 years ago

marcelpi commented 8 years ago

Clearly the magenta is the dominant color, but color-thief indicates blue. screenshot 2016-01-27 23 05 55 (url)

marcelpi commented 8 years ago

Another weird behavior. Check the dominant color. screenshot 2016-01-27 23 15 17

andgatjens commented 8 years ago

Happens the same here:

screen shot 2016-03-09 at 12 15 54 pm
jonscottclark commented 8 years ago

@marcelpi I think the larger issue is how many tabs you have open! :astonished:

But in all seriousness, this is weird!

jsobus-neurosys commented 8 years ago

I got a lot of wrong dominant colors - using it for logos, to get main logo color. All time dominant colors are very different from that on image. Please review your alghoritm. I found that pngs are working better then jpgs. Look at this example with google logo:

google-logo

Dominant color is not valid for both but 1st palette color for png is ok - shouldn't 1st pallete color be dominant one?

Link to original png: http://medienrauschen.de/wp-content/uploads/2015/09/google-new-logo.png

tea3 commented 7 years ago

+1

henrikhelmers commented 7 years ago

I ended up with more reliable results by counting colors instead of using color-thief. Attaching code in case others want to experiment or build upon it.

_getPalette = (image) => {
  const canvas = document.createElement('canvas');
  const size = 16;
  const maxPaletteSize = 10;
  const context = canvas.getContext('2d');
  const pixelArray = []; // Contains arrays of [red, green, blue, freqency]
  const palette = []; // Contains arrays of [red, green, blue]

  canvas.width = size;
  canvas.height = size;
  context.imageSmoothingEnabled = false;
  context.drawImage(image, 0, 0, size, size);

  // Format is [r,g,b,a,r,g,b,a,...]
  const pixels = context.getImageData(0, 0, size, size).data;

  for (let i = 0; i < pixels.length / 4; i++) {
      const offset = i * 4;
      const red = pixels[offset];
      const green = pixels[offset + 1];
      const blue = pixels[offset + 2];
      const alpha = pixels[offset + 3];
      let matchIndex = undefined;

      // Skip this pixel if transparent or too close to white
      if (alpha === 0 || (red > 240 && blue > 240 && green > 240)) {
        continue;
      }

      // See if the color is already stored
      for (let j = 0; j < pixelArray.length; j ++) {
        if (red === pixelArray[j][0] &&
            green === pixelArray[j][1] &&
            blue === pixelArray[j][2]) {
          matchIndex = j;
          break;
        }
      }

      // Add the color if it doesn't exist, otherwise increment frequency
      if (matchIndex === undefined) {
        pixelArray.push([red, green, blue, 1]);
      } else {
        pixelArray[matchIndex][3]++;
      }
  }

  // Sort pixelArray by color frequency
  pixelArray.sort(function(a, b) {
    return b[3] - a[3];
  });

  // Fill array with [red, green, blue] values until maxPaletteSize or
  // until there are no more colors, whichever happens first
  for (let i = 0; i < Math.min(maxPaletteSize, pixelArray.length); i++) {
    palette.push([pixelArray[i][0], pixelArray[i][1], pixelArray[i][2]]);
  }

  return palette;
};
mt-ttmy commented 4 years ago

I'm having probably a related issue. The dominant color shoud be white or a color in the "whites" range. I'm using Color Thief to determine whether to use black or white text on my image, using extracted dominant color as reference.

Schermata 2020-04-17 alle 10 44 15