Get the most dominant color!

7

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!

Arjun

Posted 2016-05-22T10:35:44.530

Reputation: 4 544

because this is kinda unclear. 3 close votes already. – Rɪᴋᴇʀ – 2016-05-22T10:52:07.783

3>

  • You should have real images. 2. You need a clear definition of "dominant", you can just have it be inferred from test cases.
  • < – Rɪᴋᴇʀ – 2016-05-22T10:52:52.993

    try clicking the little icon that kinda looks like a painting/background? Or link the image and I will do it. – Rɪᴋᴇʀ – 2016-05-22T11:05:20.070

    @EᴀsᴛᴇʀʟʏIʀᴋEdited. – Arjun – 2016-05-22T11:45:28.183

    Related. – Leaky Nun – 2016-05-22T11:54:42.370

    Any image file format? – edc65 – 2016-05-22T13:48:35.790

    @edc65 Only .png. – Arjun – 2016-05-22T14:14:16.383

    Could we have the image file contents as input, rather than the path, so that languages without file I/O can compete? – Mego – 2016-05-23T00:49:59.893

    @Mego Yes, you can! – Arjun – 2016-05-23T01:16:50.930

    If the language can only load an image from a path asynchronously, can we write a function that returns the value like function dominant(url,function callback(color){...}) where callback() is an input function? – Patrick Roberts – 2016-05-23T06:14:19.930

    @PatrickRoberts Yes, you can! – Arjun – 2016-05-23T06:19:00.467

    Answers

    4

    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).

    Leaky Nun

    Posted 2016-05-22T10:35:44.530

    Reputation: 45 011

    4

    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="http://i.stack.imgur.com/rqsAX.png">
    <img>
    <input class=o disabled>
    <input class=i disabled value="http://i.stack.imgur.com/KECpJ.png">
    <img>
    <input class=o disabled>
    <input class=i disabled value="http://i.stack.imgur.com/DRCWd.png">
    <img>
    <input class=o disabled>
    <input class=i id=i placeholder="Try any image!">
    <img>
    <input class=o id=o disabled>

    Patrick Roberts

    Posted 2016-05-22T10:35:44.530

    Reputation: 2 475

    Nice answer +1 :) – Arjun – 2016-05-23T09:16:35.253

    See http://meta.stackexchange.com/a/267808/313880 for proxying CORS for image tests.

    – Patrick Roberts – 2016-05-25T06:00:55.857

    Just to clarify, my demo no longer works because the crossorigin.me CORS service appears to be offline. – Patrick Roberts – 2016-06-04T17:23:42.067

    3

    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.

    David

    Posted 2016-05-22T10:35:44.530

    Reputation: 1 316

    Should be able to be golfed more, but I can't quite get it... – David – 2016-05-23T00:23:06.673

    You can add a screenshot or a link to a screenshot to prove that it works. Thanks for answering! :) – Arjun – 2016-05-23T00:24:28.380

    2

    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:http://i.imgur.com/Zr3tvED.gifv

    Keatinge

    Posted 2016-05-22T10:35:44.530

    Reputation: 481

    @Sting sorry that's what I meant, Image 2 is equal and image 3 prints (255,255,255) – Keatinge – 2016-05-23T03:43:43.510

    @Sting I don't really understand what you mean by a screenshot to prove it works? You want just a screenshot of console output? – Keatinge – 2016-05-23T03:44:22.150

    2

    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

    pgy

    Posted 2016-05-22T10:35:44.530

    Reputation: 830

    2

    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

    AdmBorkBork

    Posted 2016-05-22T10:35:44.530

    Reputation: 41 581

    1

    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: http://i.stack.imgur.com/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)))))
    

    mark

    Posted 2016-05-22T10:35:44.530

    Reputation: 251