15
\$\begingroup\$

This one is simple to describe and I could hardly believe that this was not golfed before:

For a given image (whatever format is supported by your language), find the unique colors and output a list of the colors.

  • Print the color code in hex RGB, that is a hash sign # followed by 2 digits for R, 2 digits for G and 2 digits for B. (#xxxxxx where x can be 0-9, A-F and/or a-f).
  • The hex code shall be followed by a space and the decimal pixel count, then a newline (\n or \r\n).
  • The list shall be sorted by count descending (most frequent color at the top).
  • If there's a tie (two colors with the same amount), the order is not important.
  • The image will not contain transparency, so any alpha value is not important. The alpha value may not be part of the output.
  • An image size of 4000x3000 must be supported (that may matter to type definitions)

Sample image "10red 20blue 30black 40white": Sample image 1

Expected output:

#FFFFFF 40
#000000 30
#0000FF 20
#FF0000 10

Sample image "blue gradient": Sample image 2

One of the expected outputs (since the order of same colors may vary):

#718DFF 19
#5571FF 15
#3855FF 11
#8DAAFF 11
#1C38FF 7
#AAAAFF 7
#8D8DFF 6
#AAC6FF 6
#C6C6FF 5
#C6E2FF 4
#001CFF 3
#E2E2FF 3
#FFFFFF 3

Sample image "testImage", kindly provided by @Luis Mendo:

Sample image 3

Expected output:

#FFFFFF 18042
#000000 1754
#FF3300 204

Similar to this question, but the output is text, not an image.

\$\endgroup\$
14
  • \$\begingroup\$ count descending is a bit unclear. How do we sort it? \$\endgroup\$
    – Riker
    Jun 4, 2016 at 14:12
  • \$\begingroup\$ @EᴀsᴛᴇʀʟʏIʀᴋ: descending: most frequent color at the top \$\endgroup\$ Jun 4, 2016 at 14:17
  • \$\begingroup\$ Okay, I would say that needs to be a bit more clear. Maybe say something like the most frequent colors at the top? \$\endgroup\$
    – Riker
    Jun 4, 2016 at 14:18
  • \$\begingroup\$ @EᴀsᴛᴇʀʟʏIʀᴋ: I agree, just updated the question for both \$\endgroup\$ Jun 4, 2016 at 14:19
  • 1
    \$\begingroup\$ A couple of test cases would be good. \$\endgroup\$ Jun 4, 2016 at 14:54

11 Answers 11

5
\$\begingroup\$

Bash + coreutils, 54

  • 7 bytes saved thanks to @manatwork
grep -oE '#\w+'|sort|uniq -c|sort -nr|awk '$0=$2" "$1'

This assumes STDIN input of Imagemagick's .txt format.

Ideone.


If the above input format is too much of a stretch, then we can add Imagemagick conversion from any format:

Bash + coreutils + Imagemagick, 71

convert $1 txt:-|grep -oE '#\w+'|sort|uniq -c|sort -nr|awk '$0=$2" "$1'

Here, the input image filename is passed in as a command-line parameter.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ awk's default OFS is a space, you can trade ` " " ` for , \$\endgroup\$
    – n0741337
    Jun 5, 2016 at 16:07
  • 1
    \$\begingroup\$ Or even shorter awk part: awk '$0=$2" "$1'. \$\endgroup\$
    – manatwork
    Jun 6, 2016 at 16:13
4
\$\begingroup\$

Mathematica, 91 bytes

StringRiffle@SortBy[Tally["#"<>IntegerString[#,16,2]&/@Join@@#~ImageData~"Byte"],-Last@#&]&

Uses a completely different method from @DavidC's answer. Unlike that one, this can support images with any size and color count. Explanation:

#~ImageData~"Byte"                   Take the RGB values of the image...
Join@@(...)                          Flatten to a list of colors...
"#"<>IntegerString[#,16,2]&/@(...)   Convert each color to #012def format...
Tally[(...)]                         Convert to a list of {color, multiplicity}
                                      pairs (this does most of the work)...
SortBy[(...),-Last@#&]               Order by the multiplicity descending...
StringRiffle@(...)                   And create a string with the desired format.
\$\endgroup\$
1
  • \$\begingroup\$ Nice. I like how "Byte" returns the image data. \$\endgroup\$
    – DavidC
    Jun 5, 2016 at 11:20
3
\$\begingroup\$

PowerShell v2+, 187 bytes

$a=New-Object System.Drawing.Bitmap $args[0]
$b=@{}
0..($a.Height-1)|%{$h=$_;0..($a.Width-1)|%{$b["#"+-join($a.GetPixel($_,$h).Name[2..7])]++}}
$b.GetEnumerator()|Sort value -des|FT -h -a

Re-uses almost all of the code from my answer on Get the most dominant color. So, please reference that for full explanation.

The changes here are in indexing $b in the third line to match the explicit output format requirements, and in the last line we sort by value in -descending order, then pipe to Format-Table with -HideTableHeaders and -AutoSize as parameters. It's very rare to actually use FT here on PPCG, since output is implicit at the end of execution, but here it's very useful to ensure we get only the correct parts output.

Examples

The first is the "red" test image from the Dominant Color challenge, the second is the "testimage" from this challenge.

PS C:\Tools\Scripts\golfing> .\color-count.ps1 C:\Tools\Scripts\golfing\img\red.png

#ff0000 139876
#dcd9cf 3056  
#f2f1ed 1512  
#ffffff 1508  
#ffe6e6 1488  
#ffe3e3 8     
#eae8e2 4     
#fffbfb 4     


PS C:\Tools\Scripts\golfing> .\color-count.ps1 C:\Tools\Scripts\golfing\img\z07VA.gif

#ffffff 18042
#000000 1754 
#ff3300 204  
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 359 355 bytes

Saved 4 bytes thanks to @Neil

u=>{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;i+=4)++o[s='#'+`00000${(d[i]<<16|d[i+1]<<8|d[i+2]).toString(16)} `.slice(-7)]?0:o[s]=1;Object.keys(o).sort((a,b)=>o[b]-o[a]).map(c=>console.log(c+o[c]))}}

Demo

f=u=>{i=new Image;i.crossOrigin='';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;i+=4)++o[s='#'+`00000${(d[i]<<16|d[i+1]<<8|d[i+2]).toString(16)} `.slice(-7)]?0:o[s]=1;Object.keys(o).sort((a,b)=>o[b]-o[a]).map(c=>console.log(c+o[c]))}}
f('http://i.imgur.com/acPudA9.gif')
<input value="https://i.imgur.com/acPudA9.gif" onchange="console.log('-------');f(this.value)">

Other test cases I uploaded to imgur to support CORS:

The specific color data on these two seems to have altered slightly when uploading for some reason but it still prints the same amount of occurrences for those two test cases.

\$\endgroup\$
3
  • \$\begingroup\$ I think | has lower precedence than << so if you use it instead of + then you won't need so many ()s. \$\endgroup\$
    – Neil
    Jun 4, 2016 at 19:47
  • \$\begingroup\$ @user2428118 next time I'd appreciate if you left concerns in a comment and asked me to edit my own answer rather than editing it yourself. You actually broke my demo by removing <!-- language: lang-js --> from the snippet, effectively disabling the JavaScript. \$\endgroup\$ Apr 8, 2017 at 17:58
  • \$\begingroup\$ @Patrick Sorry, I will be more careful next time. Also, I just found a way to save some bytes: u=>{document.write`<img src=${u} id=i><canvas id=e>`;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;i+=4)++o[s='#'+`00000${(d[i]<<16|d[i+1]<<8|d[i+2]).toString(16)} `.slice(-7)]?0:o[s]=1;Object.keys(o).sort((a,b)=>o[b]-o[a]).map(c=>console.log(c+o[c]))}} \$\endgroup\$ Apr 8, 2017 at 18:30
2
\$\begingroup\$

Pyth, 29 bytes

jmj;_d_SrSm+\#.[\06.Hid256'Q8

Try it online!

(The online interpreter cannot read image, so I stripped of that part and inputted the result of reading that image, which is a list of color triplets. The part responsible for reading the image is ', a single colon.)

Proof of functionality of ' a single colon

\$\endgroup\$
2
  • \$\begingroup\$ You need a s before 'Q, since an image is represented as list of lists of triplets. \$\endgroup\$
    – Jakube
    Jun 4, 2016 at 18:45
  • \$\begingroup\$ Here's a correct (and shorter) solution V_SrS%L+\#*3"%02X"s'z8jd_N \$\endgroup\$
    – Jakube
    Jun 4, 2016 at 19:04
2
\$\begingroup\$

Mathematica 103 92 bytes

{"#"<>IntegerString[255Round[List@@#],16,2],#2}&@@@DominantColors[#,9999,{"Color","Count"}]&

Example

pic


Explanation

DominantColors normally returns a list of colors and counts representing the main clusters in a picture. When the number of colors requested exceeds the number of colors in the image, the exact pixel colors are returned. (I am assuming that fewer than 10 000 colors will be in the input image.)

picture


{"#"<>IntegerString[255Round[List@@#],16,2],#2} converts the base 10 color values with hexadecimal values.


Note: there are only 5 colors in the mandrill image. (I used ColorQuantize[<image>,5] to reduce the number of colors in the standard mandrill image.)

\$\endgroup\$
2
  • \$\begingroup\$ Just out-golfed you ;) \$\endgroup\$ Jun 5, 2016 at 0:54
  • \$\begingroup\$ By a little bit. But your approach takes an unlimited number of colors. \$\endgroup\$
    – DavidC
    Jun 5, 2016 at 12:23
1
\$\begingroup\$

Java (1.4+), 483 428 bytes

import java.util.*;class I {public static void main(String[] a) throws Exception {java.awt.image.BufferedImage i = javax.imageio.ImageIO.read(new java.io.File(a[0]));Map m=new HashMap();String s;for(Integer x=0,y=0,c;y<i.getHeight();y++)for(x=0;x<i.getWidth();m.put(s=x.toHexString(((c=i.getRGB(x++,y))&0xff0000)>>16)+x.toHexString((c & 0xff00)>>8)+x.toHexString(c&0xff),m.get(s)==null?1:(int)m.get(s)+1));System.out.print(m);}}

Try it online! (Doesn't work online)


Ungolfed:

import java.util.*;

class I {
    public static void main(String[] a) throws Exception {
        java.awt.image.BufferedImage i = javax.imageio.ImageIO
                .read(new java.io.File(a[0]));
        Map m = new HashMap();
        String s;
        for (Integer x = 0, y = 0, c; y < i.getHeight(); y++)
            for (x = 0; x < i.getWidth(); m
                    .put(s = x.toHexString(((c = i.getRGB(x++, y)) & 0xff0000) >> 16)
                            + x.toHexString((c & 0xff00) >> 8)
                            + x.toHexString(c & 0xff), m.get(s) == null ? 1
                            : (int) m.get(s) + 1))
                ;
        System.out.print(m);
    }
}

The toString() of map outputs like this:

{7c7c7c=6, 1d57a5=20468, 121212=7, d3d3d3=3, bdbdbd=9, 949494=2, 333=14, 626262=3, cacaca=2, 141414=5, fff=11, c9c9c9=1, e8e8e8=1, 919191=4, 161616=5, c2c2c2=1, 646464=7, 979797=12, fafafa=2, 808080=1, 7b7b7b=1, 484848=4, b9b9b9=2, f1f1f1=2, 6b6b6b=6, 363636=15, 262626=4, d8d8d8=2, 868686=4, 757575=1, 575757=3, a7a7a7=2, cecece=2, dcdcdc=2, c3c3c3=2, 1d1d1d=5, 727272=9, 656565=2, 3a3a3a=3, 7d7d7d=10, 393939=5, 797979=3, 222=31, 8f8f8f=2, 454545=4, 181818=9, 2e2e2e=2, 222222=1, 1c1c1c=19, b8b8b8=2, e1e1e1=5, 232323=5, 8a8a8a=3, 959595=7, 6a6a6a=9, 434343=7, 5c5c5c=3, 111=20, 909090=3, 424242=4, 212121=1, 1a1a1a=6, 202020=7, efefef=1, 565656=5, 6e6e6e=7, 767676=3, 323232=2, eee=5, 444=18, 2c62ab=1, 717171=2, b1b1b1=3, 6c6c6c=3, 545454=7, 515151=17, 2f2f2f=2, 4a4a4a=3, 888888=6, 6d6d6d=3, 898989=3, a3a3a3=5, 7e7e7e=9, ddd=9, b6b6b6=3, 2b2b2b=5, 313131=5, 8d8d8d=1, a2a2a2=2, 696969=3, a5a5a5=3, 4f4f4f=5, 828282=7, 191919=5, 606060=4, 6f6f6f=4, 8b8b8b=3, ebebeb=2, 555=19, 929292=3, 131313=11, 999999=5, d2d2d2=2, 444444=9, 474747=4, dddddd=1, 585858=8, 5a5a5a=3, 000=9887, afafaf=2, dfdfdf=3, 747474=3, 666666=4, a1a1a1=4, 2a2a2a=11, 4d4d4d=6, 818181=2, 878787=5, 215aa6=1, d9d9d9=4, b5b5b5=3, b4b4b4=3, 737373=4, aeaeae=3, bbb=15, 242424=4, 2d2d2d=8, 888=19, c1c1c1=1, 494949=9, dbdbdb=5, ccc=19, 5d5d5d=3, 5f5f5f=1, 414141=6, c8c8c8=3, aaa=16, 1e1e1e=3, 707070=2, 9e9e9e=2, 373737=7, 9d9d9d=2, 1b1b1b=4, 303030=7, 535353=10, 595959=2, 8e8e8e=3, 383838=5, 939393=18, 616161=2, 686868=6, dadada=1, e3e3e3=2, 5b5b5b=3, a4a4a4=5, 8c8c8c=5, a6a6a6=11, 292929=6, 4c4c4c=3, 151515=6, fefefe=2, 787878=2, 505050=2, e2e2e2=1, 1f1f1f=9, adadad=2, ababab=1, 5e5e5e=6, 252525=4, 4e4e4e=3, 282828=7, a8a8a8=4, 9c9c9c=3, aaaaaa=1, 101010=5, b7b7b7=2, 969696=6, 7f7f7f=4, 555555=2, a9a9a9=5, 343434=8, 999=17, 777777=3, ffffff=76669, f0f0f0=4, bbbbbb=1, 1e58a5=1, b3b3b3=4, 777=20, 636363=2, d4d4d4=1, 2c2c2c=5, 848484=1, 3c3c3c=3, bfbfbf=2, 3e3e3e=9, 333333=4, 7a7a7a=3, 858585=4, 4b4b4b=3, 272727=7, 111111=6, 666=13, 9b9b9b=1, bcbcbc=4, cfcfcf=2, 9a9a9a=1, 404040=21, 525252=3, 989898=4, 171717=5, 3b3b3b=2, c4c4c4=1, 3f3f3f=7, 464646=1, cdcdcd=2, b2b2b2=33, c5c5c5=2, bababa=2}

Please don't post 1.8 specific golfs suggestions, unless it works in older Java, me no want.

Example: Lambdas don't work in more versions of Java than they do work in.

\$\endgroup\$
6
  • \$\begingroup\$ "Please don't post 1.8 specific golfs suggestions, unless it works in older Java, me no want." Out of curiosity: why Java 4 while Java 10 is already available? \$\endgroup\$ Mar 22, 2018 at 16:01
  • \$\begingroup\$ Some golfs that should work in Java 4 (I think): import java.util.*;class M{public static void main(String[]a)throws Exception{java.awt.image.BufferedImage i=javax.imageio.ImageIO.read(new java.io.File(a[0]));Map m=new HashMap();String s;for(Integer x=0,y=0,c;y<i.getHeight();y++)for(x=0;x<i.getWidth();m.put(s=x.toHexString((c&0xff0000)>>16)+x.toHexString((c&0xff00)>>8)+x.toHexString(c&0xff),m.get(s)==null?1:(int)m.get(s)+1))c=i.getRGB(x++,y);System.out.print(m);}} (419 bytes) \$\endgroup\$ Mar 22, 2018 at 16:07
  • \$\begingroup\$ @KevinCruijssen Because 1.8 was, arguably, the biggest release in terms of code that will not run with previous versions. Most other releases were fixes and additions of classes to the JRE. 1.8 was the least compatible with previous JRE's. \$\endgroup\$ Mar 22, 2018 at 17:25
  • \$\begingroup\$ @KevinCruijssen x.toHexInteger was smarter than a static import. \$\endgroup\$ Mar 22, 2018 at 17:26
  • \$\begingroup\$ Also not Java 3, because Java 3... really... has so few shortcuts... \$\endgroup\$ Mar 22, 2018 at 17:40
1
\$\begingroup\$

Tcl/Tk, 134 bytes

console s
lmap s [concat {*}[[image c photo -fi $argv] d]] {dict inc D $s}
dict fo k\ v [lsort -int -s 2 -inde 1 -de $D] {puts $k\ $v}

d is for data.

Tcl/Tk, 232 bytes

console s
set I [image c photo -fi $argv]
set i 0
time {set j 0
time {dict inc D #[join [lmap s [$I g $i $j] {format %02X $s}] ""]
incr j} [image h $I]
incr i} [image w $I]
dict fo k\ v [lsort -int -s 2 -inde 1 -de $D] {puts $k\ $v}

wish sort_pix.tcl QTE4O.png

enter image description here


wish sort_pix.tcl 5s1Ob.png

enter image description here


wish sort_pix.tcl z07VA.gif

enter image description here

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

Brain-Flak, 1110 bytes

{{}({}[(()()()()()){}])}{}(<>)<>([]){{}({}()<({}()<({}()<>)>)>)<>([])}{}<>{({}<({}<({}<(()()<>)>)>)>)<>{(<>)<>(()()()){({}[()]<<>({}<([([(({})<>[({})]<>)])]<>)<>{({}()<([{}])<>({}<>)<>>)<>}{}<>{}<>>{})({}<({}<({}<>)<>({}<>)<>({}<>)<>>)<>({}<>)<>({}<>)<>({}<>)>)<>({}<({}<>)<>({}<>)<>>)<>({}<>)<>({}<>)>)}{}<>((){[()](<{}>)}{})((){[()](<{}({}())<>{}{}{}>)}{}){{}(<<>(()()()){({}[()]<({}<(([])<{{}({}<>)<>([])}{}<>>)<>>)<>{({}[()]<({}<>)<>>)}{}<>>)}{}>)}{}<>({}<({}<>)<>({}<>)<>({}<>)<>>)<>({}<>)<>({}<>)<>({}<>)<>}({}<(([])<{{}({}<>)<>([])}{}<>>)<>>)<>{({}[()]<({}<>)<>>)}{}(()()()()){((({}[()])<{({}[()]<({}<({}<>)<>>)>)}{}>)<{({}[()]<<>({}<>)>)}{}>)}{}<>}{}<>(()()()())([][()])({}(<>))<>([()]{()<(({})){({}[()])<>}{}>}{}<><{}{}>){({}[()]<(<(()()()())([][()])({}(<>))><>([()]{()<(({})){({}[()])<>}{}>}{}<><{}{}>)<{({}[()]<([([({}<({}<({}<({}<(({})<>)<>>)>)>)>)<><({}<>)>]{}<(())>)](<>)){({}())<>}{}({}<><{}{}>){{}<>(<({}<({}<({}<({}<({}<({}<({}<({}<>)>)>)>)<>>)>)>)>)<>({}<({}<({}<({}<<>>)>)>)>)>)}{}({}<({}<({}<({}<>)>)>)>)<>>)}{}<>{}{}{}{}>[()]){({}[()]<({}<({}<({}<({}<>)>)>)>)<>>)}{}<>>)}{}{({}[()]<>)<>}<>

Try it online!

Well that was an adventure. Turns out, Brain-Flak is not very good at image processing. Who knew?

I'll start off by noting that this technically doesn't meet the strict output format requested. If you want that enforced let me know and I'll try to add translation code. For now it outputs decimal numbers: each 4 numbers represents a color in the order RED GREEN BLUE COUNT.

Next up, input. Allowed input formats were more flexible so I used the format easiest for Brain-Flak to parse (that I could find): Netpbm P6. Unfortunately, Brain-Flak could not parse decimal numbers from the P3 format because all Netpbm images start with the character P and Brain-Flak can't handle decimal input from files containing non-numeral characters. So P6 was used instead, because P6 stores color information as bytes, which are treated as numbers in ASCII input mode. There remained a challenge because the header information was not encoded usefully, but fortunately I didn't need any of that information so it is just discarded. This program doesn't fully adhere to the Netpbm standard because it does not allow newlines within the header, but newlines are not required by the standard so the inputs are valid Netpbm files.

Last caveat, the version on TIO is actually not correctly configured for "official" operation because (to my knowledge) I can't supply files as input on TIO, nor could I supply bytes corresponding to unprintable ASCII in the direct input. For official operation the -a flag is needed to take input as raw bytes and -f to take input from a file. The sample input on the TIO link was instead manually translated from the example on the Netpbm wiki page.

Additionally, I'd like to thank the Brain-Flak wiki for providing helpful snippets of code to work from. In particular, the Bubble-Sort implementation here was instrumental to the final step once I had gotten a count for every color, since I didn't really have any idea where to start otherwise. It took heavy modification, but I'm glad I didn't have to start from scratch.

Here is the ungolfed and commented version of the code. Brain-Flak is a bit too verbose to include a usefully formatted explanation right in this post, but the ungolfed version on TIO contains everything I would include in one with better formatting than I could manage here, so if you're interested please take a look.

There may be golfing yet to do, my previous Brain-Flak answer went through a lot of revisions, but hopefully the lessons learned there gave this a better starting point.

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

Python 2, 186 bytes

import Image
I=Image.open(input()).convert('RGB')
w,h=I.size
r=['#'+('%0.2X'*3)%I.getpixel((i%w,i/h))for i in range(w*h)]
for a,b in sorted(set((r.count(v),v)for v in r))[::-1]:print b,a

Try it online!

Disclaimer: Presented outputs are one-liners for readability. Code outputs result with space and line separation as requested by challenge.

Output for 10red 20blue 30black 40white:

[('#FFFFFF', 40), ('#000000', 30), ('#0000FF', 20), ('#FF0000', 10)]

Ouput for blue gradient:

[('#718DFF', 19), ('#5571FF', 15), ('#8DAAFF', 11), ('#3855FF', 11), ('#AAAAFF', 7), ('#1C38FF', 7), ('#AAC6FF', 6), ('#8D8DFF', 6), ('#C6C6FF', 5), ('#C6E2FF', 4), ('#FFFFFF', 3), ('#E2E2FF', 3), ('#001CFF', 3)]

Output for test image

[('#FFFFFF', 18042), ('#000000', 1754), ('#FF3300', 204)]

Explanation:

w,h=I.size # field size stores tuple of values of width and height of image

I.getpixel((i%w,i/h)) # returns tuple of base10 RGB values

('%0.2X'*3) # format string to convert int into hex

set((r.count(v),v)for v in r) # generate set of unique pairs count-color 

sorted(set(...))[::-1] # apply sorted(), as it sorts set of tuples by firts elements and reverse sort

print b,a  # swap values in tuples and print
\$\endgroup\$
0
\$\begingroup\$

SmileBASIC, 165 bytes

DEF C A
L=LEN(A)DIM C[L],N[L]FOR J=0TO L-1FOR I=0TO U-1ON A[J]-C[I]GOTO@L
NEXT
U=U+1@L
C[I]=A[J]INC N[I]NEXT
RSORT.,U,N,C
FOR I=0TO U-1?"#";HEX$(C[I],6),N[I]NEXT
END

Image is given as an array of 32 bit ARGB color values (the alpha value is trimmed when the number is converted to a 6 digit hex string)

\$\endgroup\$

Your Answer

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

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