Construindo um mecanismo de busca de imagens baseado em cores em Ruby
Publicados: 2022-03-11Dizem que uma imagem vale mais que mil palavras. E de muitas maneiras as palavras nas imagens são cores. As cores são parte integrante da nossa vida, e não podemos negar a sua importância.
Ao olhar para uma imagem, muitas vezes tentamos identificar a cor em uma parte dela. Todos nós tentamos fazê-lo, mas nunca o fizemos em detalhes. Quando solicitados a identificar as cores de uma imagem, tendemos a rotulá-las usando nomes de cores específicos, como vermelho, azul e verde. No entanto, se formos solicitados a extrair as 30 cores mais proeminentes de uma imagem, nosso olho não poderá detectá-las ou identificá-las com a mesma facilidade. Camalian é tudo sobre isso. Ele ajuda você a extrair cores de imagens e depois brincar com elas.
Neste artigo, vamos dar uma olhada no que são os espaços de cores, o que a gema Ruby Camalian tem a oferecer e como ela pode ser usada para fazer um mecanismo de busca de imagens simples que usa cores para identificá-las e distingui-las.
Espaço de cores
Antes de começarmos, vamos primeiro entender alguns conceitos básicos sobre cores. As imagens apresentadas em nossas telas são representadas usando uma matriz bidimensional de pixels. Embora os arquivos de imagem possam ser codificados de maneiras diferentes, a representação bruta após descompactar e decodificar os dados é a mesma. Nesta representação baseada em matriz 2D, cada pixel em uma imagem colorida tem três componentes: vermelho, verde e azul. Embora as imagens impressas em papel também sejam uma superfície bidimensional de pontos, os próprios pontos geralmente são uma mistura de tintas de quatro componentes: ciano, magenta, amarelo e preto. Estas entre algumas outras técnicas diferentes são usadas para representar cores são chamadas de espaços de cores. Alguns dos espaços de cores mais usados são RGB, CMYK, HSL e HSV. CMYK é usado principalmente na indústria de impressão, enquanto todos os outros são usados em mídia digital.
Espaço de cores RGB
Qualquer mídia eletrônica física, como telas CRT, LCDs ou telefones que transmitem luz, produz cores usando três componentes: vermelho, verde e azul. O olho humano pode detectar milhões de cores estimulando três tipos de receptores de cores no olho. Você pode relacionar esses receptores a R, G e B.
Idealmente, cada componente de cor é armazenado em um byte cujos valores podem variar entre 0 e 255.
Espaço de cores HSL e HSV
Organizar o espaço de cores RGB em um cubo é bastante desafiador. Os resultados de tentar representá-lo em um cubo e ou roda de cores são ruins. Ao trabalhar com milhões de cores, cada tonalidade de cor não pode ser alinhada corretamente no espaço de cores RGB.
Para superar esse problema, na década de 1970, os pesquisadores introduziram os espaços de cores HSV (Hue, Saturation, Value) e HSL (Hue, Saturation, Lightness). Ambos os espaços de cores podem ser alinhados adequadamente em uma roda de cores e facilitam a identificação de vários tons de cores nela.
Rubi para cores
Camalian é tudo sobre cores. Uma das coisas mais simples que você pode fazer com Camalian é identificar cada tonalidade de cor usada em uma imagem.
Digamos que temos uma imagem com 15 cores distintas.
Identificar as cores da amostra é certamente mais fácil do que identificá-las a partir da própria imagem. Além disso, esta é uma imagem simples, e as fotos reais capturadas geralmente são muito mais diversas quando se trata de sua paleta de cores. Extrair os valores de cor da imagem requer alguns pedaços de código bastante complicados, e é aí que entra o Camalian. Ele faz essas coisas complicadas para que você possa extrair informações relacionadas à cor de uma imagem com facilidade.
Começando
Se extrair cores com Camalian é fácil, instalar com ele é ainda mais fácil. Você pode instalar a gem Ruby executando:
gem install camalian
E para usar esta gem, você pode requerê-la diretamente em seu script Ruby:
require 'camalian'
Extraindo cores
Para extrair cores de uma imagem, primeiro precisamos carregá-la na memória e usar métodos no objeto de imagem:
image = Camalian::load( File.join( File.dirname(__FILE__), 'colormap.png') ) colors = image.prominent_colors(15) puts colors.map(&:to_hex)
Este trecho de código carrega uma imagem chamada “colormap.png” do diretório onde o script reside e extrai as 15 cores mais proeminentes dele.
Para executá-lo, salve o arquivo como “color_test1.rb” e execute-o no shell por ruby color_test1.rb
. Ele deve produzir uma saída semelhante à seguinte:
["#318578", "#41b53f", "#2560a3", "#359169", "#2154b1", "#4dda15", "#1d48bf", "#1530dc", "#296d94", "#193dcd", "#3da94d", "#45c131", "#3da84e", "#2d7986", "#193cce"]
E é tão fácil! Acabamos de extrair 15 cores usadas na imagem acima. Você pode imaginar tentar fazer isso usando código maluco, ou pior, com os olhos? Vamos discar as coisas por um entalhe. Desta vez, tentaremos usar Camalian em uma imagem com mais detalhes:
Ao executar o mesmo script nesta imagem, obtemos o seguinte:
[“#210b03”, “#723209”, “#974d09”, “#ae5d08”, “#c77414”, “#d77f15”, “#ffea54”, “#94651f”, “#b66a15”, “#c25f06”, “#fdd94d”, “#d39a39”, “#efa540”, “#fffffe”, “#fff655”]
A tentativa de visualizar a matriz de valores de cores produzida acima nos dá algo assim:
A paleta é boa, mas não há padrão específico nas cores extraídas. Vamos classificar os valores de cores por similaridade e ver se isso ajuda. Tudo o que precisamos fazer é chamar mais uma função antes de imprimir o array no console:

colors = image.prominent_colors(15).sort_similar_colors
Mas e se quiséssemos extrair cores relativamente mais claras? Pode ser que queiramos cores que sejam apenas 40% escuras, ou em outras palavras, que tenham um valor de claridade (no espaço de cores HSL) entre 0 e 40. Tudo o que precisamos fazer é:
colors = image.prominent_colors(15).light_colors(0, 40)
Fazendo o mecanismo de busca de imagens
Agora que sabemos como é fácil lidar com cores usando Camalian, vamos construir uma aplicação web simples que permite fazer upload de imagens e indexá-las por cor. Por brevidade, vamos pular os vários detalhes envolvidos na construção de um aplicativo Ruby. Em vez disso, focaremos em especificidades que lidam com cores e uso camaliano.
Quanto ao escopo deste aplicativo Ruby, vamos limitá-lo a lidar com uploads de imagens, extrair cores da imagem antes de armazená-las e procurar imagens carregadas com base na cor e no limite escolhidos.
Abaixo está um diagrama de modelo para explicar a estrutura de nossa aplicação:
Cada imagem carregada é representada usando um objeto PortfolioItem. Cada objeto Color representa cores exclusivas descobertas por meio de imagens carregadas e, finalmente, PortfolioColor representa a relação entre cada imagem e as cores encontradas nela.
A maioria das partes do aplicativo são bastante padrão, especialmente quando se trata de lidar com uploads de imagens, criar entidades de modelo e mantê-las no banco de dados, etc. Caso você seja um desenvolvedor Ruby, isso não deve ser fácil. Abaixo está o método usado para extrair a cor da imagem enviada:
after_save :extract_colors private def extract_colors image = Camalian::load(self.image.path) colors = image.prominent_colors(self.color_count.to_i).sort_similar_colors colors.each do |color| unless c = Color.where(r: color.r, g: color.g, b: color.b).first c = Color.create(r: color.r, g: color.g, b: color.b, h: color.h, s: color.s, l: color.l) end self.colors << c end end
Isso ajuda extraindo a paleta de cores e salvando no banco de dados. Observe como extraímos apenas um número específico de cores proeminentes (algo que o usuário pode definir ao fazer o upload da imagem).
Quando um usuário envia uma imagem por meio do formulário na interface do usuário da Web, a imagem é recebida por meio de um manipulador de postagem e um novo PortfolioItem é criado para ela. Esse método, extract_colors , é invocado sempre que um item do portfólio é persistido no banco de dados.
Para poder renderizar a paleta de cores nas páginas, usamos um simples auxiliar:
module PortfolioItemsHelper def print_color_palette(colors) color_string = '' colors.each do |c| color_string += content_tag :span, ' ', style: "display: block; float: left; width: 35px; height: 35px; background: #{c.to_hex}" end content_tag :div, color_string.html_safe, style: "display: inline-block;" end end
Essencialmente, cria uma div com pequenos intervalos quadrados, cada um com sua cor de fundo definida para uma das cores proeminentes extraídas.
Finalmente, para implementar a pesquisa, temos que usar alguma matemática e lógica:
class PortfolioSearchForm include ActiveModel::Model attr_accessor :color, :similarity validates_presence_of :color, :similarity def color_object @color_object ||= Camalian::Color.new(self.color) end def color_range(color, level) (color_object.send(color) - level)..(color_object.send(color) + level) end def colors_by_rgb level = self.similarity.to_i * 255 / 100.0 Color.where(r: color_range(:r, level), g: color_range(:g, level), b: color_range(:b, level)) end def colors_by_hsl level = self.similarity.to_i Color.where(h: color_range(:h, (self.similarity.to_i * 30 / 100.0) ), s: color_range(:s, level), l: color_range(:l, level)) end end
Com o método colors_by_hsl , podemos buscar todas as entidades Color que correspondem à nossa consulta. E, com eles, podemos identificar todas as imagens carregadas e renderizar nossa página de resultados de pesquisa. A consulta em si é bastante simples. Dada uma cor específica e um valor de similaridade, um intervalo de valores é calculado para cada componente de cor.
E isso é praticamente todas as partes difíceis.
Experimentando
Você pode encontrar o código completo no GitHub. Você pode implantar uma instância deste aplicativo no Heroku ou experimentá-lo localmente:
git clone https://github.com/nazarhussain/camalian-sample-app.git cd camalian-sample-app bundle install rake db:migrate rails s
A aplicação final se parece com isso:
Quando o aplicativo estiver em execução, aponte seu navegador da Web para http://localhost:3000. Usando o formulário na tela, carregue algumas imagens de paletas de cores variadas. Em seguida, para pesquisar imagens por cor, use o campo de pesquisa no canto superior direito. A lista suspensa de limite permite especificar a tolerância de dissimilaridade para combinar as cores das imagens.
Qual é o próximo?
O aplicativo de demonstração que construímos neste artigo é bastante simples, mas as possibilidades são infinitas! Alguns outros usos práticos desta biblioteca podem incluir:
- Restringindo usuários de fazer upload de fotos de perfil escuras
- Adapte o tema de cores do site a alguma foto enviada pelo usuário
- Para competições de design, valide automaticamente os envios de acordo com os requisitos da paleta de cores
Você pode explorar ainda mais a biblioteca no GitHub e conferir seu código-fonte. Sinta-se à vontade para reportar bugs criando problemas ou contribuir enviando pull requests.