7
\$\begingroup\$

In this challenge, your task is to write a program that takes in an image and returns the most dominant color in it. Dominant color here means the color with which most of the image is covered (see the test cases for a better understanding).


Input

An image's path. You'll have to load that image in your program. You can take the input as STDIN, function argument or whatever suits your language. You may assume that the image will have only .png extension.


Output

The most dominant color in any format. (But it should be accurate, you can't return Red for #f88379). The type of output depends on you. If there is an equal distribution of colors in the image or there are more than one dominant color, then your program should return "Equal". You can show the output by printing on screen, logging on command line or console, or whatever suits you.


Test Cases

Red

=> Red/#ff0000/rgba(255,0,0)

Equals.

=> "Equal"

White

=> White (or the same color in any other format)

In these test cases, I have posted images rather than image paths, but you'll be given image paths only as input.


Note If your language contains a built-in for this, then you must NOT use it. Please add a screenshot or a link to a screenshot to prove that your program works.


This is , so the shortest answer in bytes wins!

\$\endgroup\$
11
  • \$\begingroup\$ because this is kinda unclear. 3 close votes already. \$\endgroup\$
    – Riker
    Commented May 22, 2016 at 10:52
  • 4
    \$\begingroup\$ 1. You should have real images. 2. You need a clear definition of "dominant", you can just have it be inferred from test cases. \$\endgroup\$
    – Riker
    Commented May 22, 2016 at 10:52
  • \$\begingroup\$ try clicking the little icon that kinda looks like a painting/background? Or link the image and I will do it. \$\endgroup\$
    – Riker
    Commented May 22, 2016 at 11:05
  • \$\begingroup\$ @EᴀsᴛᴇʀʟʏIʀᴋEdited. \$\endgroup\$
    – Arjun
    Commented May 22, 2016 at 11:45
  • \$\begingroup\$ Related. \$\endgroup\$
    – Leaky Nun
    Commented May 22, 2016 at 11:54

7 Answers 7

4
\$\begingroup\$

Pyth, 18 bytes

FGITW.

?tJeM.MhZrS'Q8\=hJ

This is how it would theoretically work offline.

This is a proof that it works.

The two links differ by whether there is a "read file command" ' (single-quote).

\$\endgroup\$
0
4
\$\begingroup\$

JavaScript (ES6), 301 328 312 bytes

(u,b)=>{i=new Image;i.src=u;e=document.createElement`canvas`;c=e.getContext`2d`;i.onload=_=>{w=e.width=i.width;h=e.height=i.height;c.drawImage(i,0,0);d=c.getImageData(0,0,w,h).data;for(o={},i=0;i<d.length;)++o[s=d.slice(i,i+=4)]?0:o[s]=1;b(o[(a=Object.keys(o).sort((a,b)=>o[b]-o[a]))[0]]==o[a[1]]?"Equal":a[0])}}

Feel free to golf further, this feels as long as Java to me personally.

This supports transparent images so alpha channel is included in output value.

The reason it's longer than before is because now that I've tested, it requires Image to contain the attribute crossOrigin, and the arguments to getImageData() are not optional.

See Meta PCG for removal of CORS from byte count.

Usage

f("http://i.imgur.com/H5OmRVh.jpg", alert)

That will asynchronously alert a color quadruplet or "Equal" after the image is loaded and counted.

The callback is not considered part of the program length because it is allowed as input by the OP in this special case.

How it works

It constructs an Image, a Canvas, and a CanvasRenderingContext2D, then draws the image to the canvas after setting its width and height correctly. It then counts occurrences of each color by implicitly converting each quadruplet to a string and generating a hash of counters. After that, it sorts the hash keys by descending count before comparing the highest two occurrences and determining whether to return the first quadruplet string in the sorted array or the string "Equal".

If the demo below displays a SecurityError, the domain of the image URL you chose does not support CORS.

Demo

f = (u, b) => {
  let i = new Image;
  i.crossOrigin = ''; // CORS added for demo
  i.src = u;
  let e = document.createElement `canvas`;
  let c = e.getContext `2d`;
  i.onload = _ => {
    w = e.width = i.width;
    h = e.height = i.height;
    c.drawImage(i, 0, 0);
    d = c.getImageData(0, 0, w, h).data;
    for (o = {}, i = 0; i < d.length;)
      ++o[s = d.slice(i, i += 4)] ? 0 : o[s] = 1;
    b(o[(a = Object.keys(o).sort((a, b) => o[b] - o[a]))[0]] == o[a[1]] ? "Equal" : a[0])
  }
}
l = i => {
  let p = i.nextElementSibling;
  p.src = i.value;
  f(`http://crossorigin.me/${i.value}`, c => p.nextElementSibling.value = c);
}
document.getElementById `i`.addEventListener('update', e => {
  l(e.target)
})
Array.prototype.slice.call(document.querySelectorAll`.i`, 0, 3).forEach(l)
input {
  display: block;
  box-sizing: border-box;
  width: 205px;
  height: 18px;
}

input.o {
  position: relative;
  top: -36px;
}

img {
  display: block;
  position: relative;
  box-sizing: border-box;
  top: -18px;
  left: 209px;
  height: 40px;
}
<input class=i disabled value="https://i.sstatic.net/rqsAX.png">
<img>
<input class=o disabled>
<input class=i disabled value="https://i.sstatic.net/KECpJ.png">
<img>
<input class=o disabled>
<input class=i disabled value="https://i.sstatic.net/DRCWd.png">
<img>
<input class=o disabled>
<input class=i id=i placeholder="Try any image!">
<img>
<input class=o id=o disabled>

\$\endgroup\$
3
  • \$\begingroup\$ Nice answer +1 :) \$\endgroup\$
    – Arjun
    Commented May 23, 2016 at 9:16
  • \$\begingroup\$ See meta.stackexchange.com/a/267808/313880 for proxying CORS for image tests. \$\endgroup\$ Commented May 25, 2016 at 6:00
  • \$\begingroup\$ Just to clarify, my demo no longer works because the crossorigin.me CORS service appears to be offline. \$\endgroup\$ Commented Jun 4, 2016 at 17:23
3
\$\begingroup\$

MATL, 30 26 23 bytes

Save some bytes by using mode with explicit output specification 6#XM.

2#YiwX:6#XMgY)tn3>?x'='

Previously (26 byte solution)

2#YiwX:S&Y'tX>=Y)tn3>?x'='

Doesn't work online, because of imread. imread can take a path or directly read a url.

Explanation:

2#Yi       % read image, returns a matrix of colours, and a matrix 
           % that indexes each pixel to the corresponding row of the colour matrix
w          % swap elements in stack
X:         % linearize index matrix to column array
6#XMg      % use mode to find most common colour indices, convert to matrix (g)
Y)         % get corresponding rows of colour matrix
tn3>       % duplicate and see if it has more than 3 elements
?          % if there is more than one most common colour
x          % delete most common colours from stack
'='        % push string onto stack
           % implicitly finish loop and display stack contents

Output is, for the first image, 1 0 0 (RGB triple), for the second =, and for the third, 1 1 1. For example, a screenshot form Matlab:

enter image description here

Note that you need to escape string delimiters when running MATL code from the Matlab command window, hence the ''=''. The > indicates MATL is asking for user input, in this I gave it the URL of the first test case, and the result is 1 0 0.

\$\endgroup\$
2
  • \$\begingroup\$ Should be able to be golfed more, but I can't quite get it... \$\endgroup\$
    – David
    Commented May 23, 2016 at 0:23
  • \$\begingroup\$ You can add a screenshot or a link to a screenshot to prove that it works. Thanks for answering! :) \$\endgroup\$
    – Arjun
    Commented May 23, 2016 at 0:24
3
\$\begingroup\$

Python 3, 186 Bytes

from PIL import Image;from collections import*;c=Counter(Image.open(input()).convert("RGB").getdata());d=c.most_common();print("equal") if d[0][1]==d[1][1] else print(c.most_common()[0])

Image 1 prints: ((255, 0, 0), 139876)
Image 2 prints: equal
Image 3 prints: ((255, 255, 255), 101809)


Ungolfed:
from PIL import Image
from collections import *
c = Counter(Image.open(input()).convert("RGB").getdata())
d = c.most_common()
print("equal") if d[0][1] == d[1][1] else print(c.most_common()[0])

Proof: enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ @Sting sorry that's what I meant, Image 2 is equal and image 3 prints (255,255,255) \$\endgroup\$
    – Keatinge
    Commented May 23, 2016 at 3:43
  • \$\begingroup\$ @Sting I don't really understand what you mean by a screenshot to prove it works? You want just a screenshot of console output? \$\endgroup\$
    – Keatinge
    Commented May 23, 2016 at 3:44
2
\$\begingroup\$

Unix tools + imagemagick, 122 bytes

convert png:- ppm:-|tail -n +4|xxd -c3 -p|sort|uniq -c|sort -n|tail -n2|awk '{S[NR]=$1}END{print S[1]==S[2]?"equal":S[2]}'

Expects the input png in stdin, prints the most dominant color in hex (RRGGBB) format.

How it works:

  • convert png:- ppm:- converts a png file to binary P6 ppm format
  • tail -n +4 skips the ppm header
  • xxd -c3 -p puts every RGB byte triplets on a new line in hex format
  • sort|uniq -c|sort -n orders the colors by their number of occurrence
  • tail -n2 gets the two most dominant colors
  • awk ...: prints the more dominant color or equal
\$\endgroup\$
0
2
\$\begingroup\$

PowerShell v2+, 215 bytes

$a=New-Object System.Drawing.Bitmap $args[0]
$b=@{}
0..($a.Height-1)|%{$h=$_;0..($a.Width-1)|%{$b[$a.GetPixel($_,$h).Name]++}}
if(($c=$b.GetEnumerator()|Sort value)[-1].value-eq$c[-2].value){"Equal";exit}$c[-1].Name

(Newlines/semicolons are equivalent, so newlines left in for clarity)

Creates a new Bitmap object, based on the input argument, stores it to $a. Creates a new empty hashtable $b, which will be used for our pixel counting.

We then loop over the image a row at a time with two loops %{...}. Each inner loop we use the .GetPixel method to return a Color object for the particular x/y coordinate we're at. We take the .Name of that color (which is a hexadecimal of the form ARGB, for example ffff0000 would be pure red fully opaque) and use that as the index into $b, incrementing that particular value by one.

So, we've now got a fully populated hashtable of pixel colors, with the values of each entry corresponding to how many pixels of that color there are. We then take $b and pump it through a Sort based on those values, and store that sorted result back into $c. If the last [-1] and second-to-last [-2] values are equal, we output "Equal" and exit. Else, we output the .Name of the last [-1] entry.

Command-line proof

\$\endgroup\$
0
1
\$\begingroup\$

Clojure, 204 bytes

(fn[n](let[p(javax.imageio.ImageIO/read(java.net.URL. n))w(.getWidth p)[[_ x][y z]](take-last 2(sort-by peek(frequencies(. p(getRGB 0 0 w(.getHeight p)nil 0 w)))))](if(= x z)(print"EQUAL")(printf"%x"y))))

prints colors as aarrggbb

Clojure isn't really great at golfing, but it's a fun language :)

proof screenshot: https://i.sstatic.net/vPgoq.png

ungolfed:

(fn[n]
  (let [p (javax.imageio.ImageIO/read (java.net.URL. n))
        w (.getWidth p)
        a (. p (getRGB 0 0 w (.getHeight p) nil 0 w))
        [[_ x] [y z]] (take-last 2 (sort-by peek (frequencies a)))]
    (if (= x z)
      (print "EQUAL")
      (printf "%x" y)))))
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.