Extracting dominant colours from pictures

by @LazarusAlon

We will use k-means in the RGB space as way a to find the most common colours in a picture. Clusters with the largest amount of elements will correspond to the dominant colours. In Julia this is done as follows.

Simple case, one picture

We load all the necessary packages.

using Pkg Pkg . add ( "Suppressor " ) 1.5s Julia Julia 1.3.1

using Plots , Images , Random , ImageMagick , Clustering using HTTP , Base64 , Suppressor 86.2s Julia Julia 1.3.1

using Plots . PlotMeasures 0.2s Julia Julia 1.3.1

And define the following function, whose output will be the first 5 more dominant colours, and their corresponding order from less common to most common.

function thiefcolors ( image ; ncolors = 5 ) img_CHWa = channelview ( image ) img_CHW = permutedims ( img_CHWa , ( 1 , 3 , 2 ) ) testmat = reshape ( img_CHWa , ( 3 , size ( image ) [ 1 ] * size ( image ) [ 2 ] ) ) sol = kmeans ( testmat , ncolors ) csize = counts ( sol ) colores = sol . centers indxc = sortperm ( csize ) colores , indxc end 0.5s Julia Julia 1.3.1 thiefcolors (generic function with 1 method)

Example

For testing we will use an image directly from a link.

url = "https://images.wallpaperscraft.com/image/autumn_drawing_walking_82963_320x480.jpg " imgtest = HTTP . download ( url ) imgtest = RGB . ( load ( imgtest ) ) 11.7s Julia Julia 1.3.1

Random . seed! ( 1021 ) colores , indxc = thiefcolors ( imgtest ) 11.1s Julia Julia 1.3.1 (Float32[0.672349 0.216812 … 0.368779 0.630687; 0.889334 0.135243 … 0.482385 0.235814; 0.932598 0.180176 … 0.652542 0.15553], [3, 5, 4, 1, 2])

Plotting the image and the first 5 dominant colours we get the following:

plot ( imgtest , aspect_ratio = 1 , grid = false , axis = :off , size = ( 400 , 400 ) ) plot! ( fill ( 400 , 5 ) , collect ( 0 : 100 : 400 ) . + 40 , m = ( :rect , 15 , stroke ( 0.1 ) ) , c = [ RGB ( colores [ : , indxc [ i ] ] ... ) for i in 1 : 5 ] , leg = false ) 27.4s Julia Julia 1.3.1

Multiple pictures

The links are in a separate file.

reflinks = linksPics.jl 0.2s Julia Julia 1.3.1 "/.nextjournal/data-named/QmbjR8J31y3E9xEXVR1pmpJsB6CgMHdiN7F4GKoe6i7BJo/linksPics.jl"

include ( reflinks ) ; 0.6s Julia Julia 1.3.1

Downloading images...

imgs = [ ] begin for i in 1 : length ( urlinks ) img = HTTP . download ( urlinks [ i ] ) push! ( imgs , RGB . ( load ( img ) ) ) end end 1.8s Julia Julia 1.3.1

Extracting color and plotting...

Random . seed! ( 1021 ) imagenes = [ ] for indx in 1 : 15 begin global colores , indxc colores , indxc = thiefcolors ( imgs [ indx ] ) end p = plot ( imgs [ indx ] , aspect_ratio = 1 , grid = false , axis = :off , size = ( 300 , 300 ) ) plot! ( fill ( 400 , 5 ) , collect ( 0 : 100 : 400 ) . + 40 , m = ( :rect , 10 , stroke ( 0.1 ) ) , c = [ RGB ( colores [ : , indxc [ i ] ] ... ) for i in 1 : 5 ] , leg = false ) push! ( imagenes , p ) end 24.2s Julia Julia 1.3.1

plot ( imagenes ... , layout = ( 3 , 5 ) , size = ( 1000 , 600 ) ) 3.7s Julia Julia 1.3.1

A simple application

First, let's download some popular images from anime.

Downloading from links in file...

imgsAnime = [ ] begin for i in 1 : length ( urlTop ) img = HTTP . download ( urlTop [ i ] ) push! ( imgsAnime , RGB . ( load ( img ) ) ) end end 2.3s Julia Julia 1.3.1

and plotting...

Random . seed! ( 1021 ) imagenesAn = [ ] for indx in 1 : 20 begin global colores , indxc colores , indxc = thiefcolors ( imgsAnime [ indx ] ) end p = plot ( imgsAnime [ indx ] , aspect_ratio = 1 , grid = false , axis = :off , size = ( 300 , 300 ) ) plot! ( fill ( 400 , 5 ) , collect ( 0 : 100 : 400 ) . + 40 , m = ( :d , 10 , stroke ( 0.1 ) ) , c = [ RGB ( colores [ : , indxc [ i ] ] ... ) for i in 1 : 5 ] , leg = false ) push! ( imagenesAn , p ) end 22.6s Julia Julia 1.3.1

plot ( imagenesAn ... , layout = ( 4 , 5 ) , rigth_margin = - 10 mm , left_margin = - 10 mm , size = ( 1000 , 900 ) ) 4.4s Julia Julia 1.3.1

indx_anime = [ 2 , 16 , 1 , 8 , 3 , 7 , 18 , 19 ] ratings = [ 9.1 , 8.8 , 8.8 , 8.6 , 8.5 , 8.1 , 8.0 , 8.5 ] ; 0.2s Julia Julia 1.3.1

Selecting 2 colours out of 5. The first and second most dominant colours.

Random . seed! ( 1021 ) picsRGBs = [ ] for indx in indx_anime begin colores , indxc = thiefcolors ( imgsAnime [ indx ] ) push! ( picsRGBs , [ imgsAnime [ indx ] , colores [ : , indxc [ 4 : 5 ] ] ] ) end end cborders = [ RGB ( picsRGBs [ i ] [ 2 ] [ : , 2 ] ... ) for i in 1 : length ( picsRGBs ) ] cfillrect = [ RGB ( picsRGBs [ i ] [ 2 ] [ : , 1 ] ... ) for i in 1 : length ( picsRGBs ) ] ; 11.6s Julia Julia 1.3.1

and plotting with the corresponding picture for each anime, the results are

figratings = plot ( ratings , st = :bar , c = cfillrect , line = ( 1 , cborders ) , bar_width = 0.7 , alpha = 0.9 , ylab = "Rating " , xticks = false , leg = false , size = ( 800 , 400 ) , ylim = ( 7 , 10 ) ) plot! ( picsRGBs [ 1 ] [ 1 ] , inset = [ ( 1 , bbox ( 0.055 , 0.05 , 0.08 , 0.22 ) ) ] , subplot = 2 , axis = false , bg = :transparent ) for p in 1 : 7 plot! ( picsRGBs [ p + 1 ] [ 1 ] , inset = [ ( 1 , bbox ( 0.055 + 0.17 * p - p * 0.055 , 0.05 , 0.08 , 0.22 ) ) ] , subplot = p + 2 , axis = false , bg = :transparent ) end figratings 5.3s Julia Julia 1.3.1

Once you know how to do it, it's simple. Find me on twitter as @LazarusAlon