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.
"$VERSION"
using Pkg
Pkg.add("Suppressor") # if something doesn't work, just add it.
using Plots, Images, Random, ImageMagick, Clustering
using HTTP, Base64, Suppressor
using Plots.PlotMeasures
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]))#input shape
sol = kmeans(testmat, ncolors)
csize = counts(sol) # get the cluster sizes
colores = sol.centers # get the cluster centers, dominat colors
indxc = sortperm(csize)
colores, indxc
end
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))
Random.seed!(1021)
colores, indxc = thiefcolors(imgtest)
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)
Multiple pictures
The links are in a separate file.
reflinks = linksPics.jl
include(reflinks);
Downloading images...
imgs = []
begin
for i in 1:length(urlinks)
img = HTTP.download(urlinks[i])
push!(imgs, RGB.(load(img)))
end
end
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
plot(imagenes..., layout = (3,5), size = (1000, 600))
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
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
plot(imagenesAn..., layout = (4,5), rigth_margin = -10mm, left_margin = -10mm, size = (1000, 900))
Ratings...
# fullmetal, one punch man, attack on titan, code geass, naruto, bleach , elfen lied, evangelion
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];
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)];
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
Once you know how to do it, it's simple. Find me on twitter as @LazarusAlon