Criando paletas de cores a partir de imagens

Já se fosse possível extrair cores automaticamente de imagens, utilizando técnicas estatística? Na verdade, isso é possível sim. Tome a foto abaixo, por exemplo, tirada da minha página sobre fotografia:

Não é difícil ver que as cores predominantes são os tons azuis, do mais claro ao mais escuro. Há um pouco de vinho e branco também. Quando questionado sobre essa paleta de cores, o computador retorna o seguinte:

Se pedirmos para ele ser mais específico, ele pode retornar uma paleta de 15 cores:

Nesse caso, estou usando uma técnica estatística de clusterização (ou agrupamento) de dados, chamada k-means. Meu curso de Introdução à Modelagem de Big Data trata dela com detalhes mas, em linhas gerais, o que está sendo feito aqui é o seguinte:

  1. A imagem é lida dentro do R
  2. Cada pixel é interpretado como um vetor em um espaço de três dimensões chamado RGB: R (red), G (green) e B (blue)
  3. Para cada centro de cluster (ou média de cores dos pixels) são calculadas as distâncias entre esse centro e as outras observações
  4. Determina-se quais observações pertencem a cada cluster
  5. Se há alguma mudança no pertencimento de um pixel a algum cluster, uma nova média é calculada
  6. Os passos 3 a 5 são repetidos até a convergência

Com isso, cada imagem considerada tem a sua paleta de cores estimada. O código capaz de fazer isso de modo semi-automático está abaixo:

library(jpeg)
library(scales)

imagem <- readJPEG("fotos/Galinhos_01.jpg")

dimensao <- dim(imagem)

imagem_rgb <- data.frame(
  x = rep(1:dimensao[2], each = dimensao[1]),
  y = rep(dimensao[1]:1, dimensao[2]),
  R = as.vector(imagem[,,1]),
  G = as.vector(imagem[,,2]),
  B = as.vector(imagem[,,3])
)

k_means <- kmeans(imagem_rgb[, c("R","G","B")], centers = 5, iter.max = 30)

show_col(rgb(k_means$centers))

Com isso, pensei em verificar quais seriam as paletas de cores fotos que coloquei na minha página sobre fotografia, de modo a encontrar algum padrão nas minhas criações. O resultado de algumas delas está a seguir:

# funcao para criar paleta de cores

paleta <- function(imagem, n, show = FALSE){
  # pacotes necessarios
  require(jpeg)
  require(scales)
  
  # le a imagem
  imagem <- readJPEG(imagem)
  
  # dimensoes da imagem
  dimensao <- dim(imagem)
  
  # encontra os valores rgb de cada pixel
  imagem_rgb <- data.frame(
    x = rep(1:dimensao[2], each = dimensao[1]),
    y = rep(dimensao[1]:1, dimensao[2]),
    R = as.vector(imagem[,,1]),
    G = as.vector(imagem[,,2]),
    B = as.vector(imagem[,,3])
  )
  
  # ecnontra 
  k_means <- kmeans(imagem_rgb[, c("R","G","B")], 
                    centers = n, 
                    algorithm = "Lloyd",
                    iter.max = 100)
  
  # mostra a paleta
  if(show){
    show_col(rgb(k_means$centers))
  }
  
  # retorna a paleta
  return(k_means$centers)
}

library(tidyverse)

arquivos <- list.files(path = "fotos/") %>%
  paste("fotos/", ., sep = "")
include_graphics(arquivos[1])

paleta(arquivos[1], n = 5, show = TRUE)

##              R            G            B
## 1 0.0009807818 0.0005065415 0.0005667995
## 2 0.4647979780 0.4464256001 0.4724367009
## 3 0.3453252695 0.4652132110 0.6127733220
## 4 0.5556619923 0.5523450580 0.5760447112
## 5 0.6501226612 0.3834856562 0.2484259031
include_graphics(arquivos[6])

paleta(arquivos[6], n = 5, show = TRUE)

##            R         G         B
## 1 0.02171297 0.5435581 0.8106518
## 2 0.11182432 0.2762389 0.3623906
## 3 0.27049240 0.6465217 0.8613597
## 4 0.80873722 0.7497839 0.6071617
## 5 0.48028306 0.4141179 0.3481294
include_graphics(arquivos[12])

paleta(arquivos[12], n = 5, show = TRUE)

##            R          G          B
## 1 0.18579488 0.22183017 0.33981866
## 2 0.12707031 0.17708101 0.29404476
## 3 0.30066878 0.32341943 0.44472720
## 4 0.24010148 0.27152639 0.39319564
## 5 0.01691153 0.01703251 0.01135607
include_graphics(arquivos[15])

paleta(arquivos[15], n = 5, show = TRUE)

##           R         G          B
## 1 0.5627367 0.3782593 0.06742293
## 2 0.8505580 0.5430173 0.03208969
## 3 0.5702232 0.4436623 0.30243428
## 4 0.9907182 0.8282073 0.02504558
## 5 0.3567449 0.2362361 0.06956810
include_graphics(arquivos[19])

paleta(arquivos[19], n = 5, show = TRUE)

##           R         G         B
## 1 0.7964889 0.6675251 0.5579351
## 2 0.4470491 0.4279966 0.4516126
## 3 0.1532425 0.2888454 0.4247624
## 4 0.8361750 0.5535849 0.3754225
## 5 0.2096430 0.2219692 0.2410029

Embora a técnica não tenha dado o resultado ideal em alguns casos, é possível extrair algumas informações interessantes. Por exemplo, aparentemente, tenho uma preferência por tons azuis e amarelos nas minhas fotos, mas poucos verdes e vemelhos. Muitos tons terrosos também aparecem nos meus trabalhos.