36

I have a question about how to get the dominant color of an image (a photo). I thought of this algorithm: loop through all pixels and get their color, either red, green, yellow, orange, blue, magenta, cyan, white, grey or black (with some margin of course) and it's darkness (light, dark or normal) and afterwards check which colors occurred the most. I think this is slow and not very precise. Is there a better way?


If it matters, it's a UIImage taken from an iPhone or iPod touch camera which is at most 5 Mpx. The reason it has to be fast is that simply showing a progress indicator doesn't make very much sense as this is for an app for people with bad sight, or no sight at all. Because it's for a mobile device, it may not take very much memory (at most 50 MB).

  • See [Material color utilities library](https://github.com/material-foundation/material-color-utilities) currently available for a few programming languages which can be used to get dominant color of an image (not necessarily the most recurring color; it extracts dominant colors appropriate for [Material 3 design system](https://m3.material.io/styles/color/dynamic-color/overview) primarily used on Android 12 and above). – Mahozad Nov 14 '22 at 17:00
  • See [this answer](https://stackoverflow.com/a/74435848/8583692). – Mahozad Nov 17 '22 at 15:43

1 Answers1

33

Your general approach should work, but I'd highlight some details.

Instead of your given list of colors, generate a number of color "bins" in the color spectrum to count pixels. Here's another question that has some algorithms for that: Generating spectrum color palettes Make the number of bins configurable, so you can experiment to get the results you want.

Next, for each pixel you're considering, you need to find the "nearest" color bin to increment. You'll need to define "nearest"; see this article on "color difference": http://en.wikipedia.org/wiki/Color_difference

For performance, you don't need to look at every pixel. Since image elements usually cover large areas (e.g., the sky, grass, etc.), you can get the result you want by only sampling a few pixels. I'd guess that you could get good results sampling every 10th pixel, or even every 100th. You can experiment with that factor as well.

[Editor's note: The paragraph below was edited to accommodate Mike Fairhurst's comment.]

Averaging pixels can also be done, as in this demo:jsfiddle.net/MUsT8/

Peter O.
  • 32,158
  • 14
  • 82
  • 96
payne
  • 13,833
  • 5
  • 42
  • 49
  • 5
    I just released a small javascript library (http://pieroxy.net/blog/pages/color-finder/index.html ) which does pretty much what you are describing. You can also pass a feedback method to skew the algo toward your color preference. Here is a live demo: http://pieroxy.net/blog/pages/color-finder/demo.html . Hope this helps. – pieroxy Jun 19 '13 at 07:39
  • 1
    Please update your answer to fix the error mentioned in the comment below. A corrected fiddle is here: jsfiddle.net/MUsT8 Our team wasted a lot of time by reacting to your demo which simply has a bug. – Mike Fairhurst Mar 26 '15 at 22:02
  • 2
    The demo of the average color shows a green background because there is a typo where the RGB background color is set so that the green and blue values are in the wrong positions. With this corrected, the "average" RGB is a blue as you would expect from the very blue example image. – Gab Apr 20 '11 at 10:12