用 Ruby 构建基于颜色的图像搜索引擎

已发表: 2022-03-11

都说一张图抵得上一千个字。 在许多方面,图片中的文字都是颜色。 颜色是我们生活中不可或缺的一部分,我们不能否认它们的重要性。

在查看图像时,我们经常尝试识别图像一部分的颜色。 我们都尝试过这样做,但从未详细做过。 当被要求从图像中识别颜色时,我们倾向于使用特定的颜色名称来标记它们,例如红色、蓝色和绿色。 然而,如果我们被要求提取图像中最突出的 30 种颜色,我们的眼睛就无法轻易地检测或识别它们。 Camalian就是这样。 它可以帮助您从图像中提取颜色,然后使用它们。

Camalian:Ruby 的 Ultiamte 颜色选择器

在本文中,我们将了解色彩空间的全部内容,Ruby gem Camalian 提供的内容,以及如何使用它来制作一个简单的图像搜索引擎,使用颜色来识别和区分它们。

色彩空间

在开始之前,让我们先了解一些关于颜色的基本概念。 我们屏幕上呈现的图像使用二维像素阵列表示。 虽然图像文件的编码方式可能不同,但对数据进行解压缩和解码后的粗略表示是相同的。 在这个基于二维数组的表示中,彩色图像中的每个像素都具有三个分量:红色、绿色和蓝色。 尽管打印在纸上的图片也是一个二维点的表面,但点本身通常是四种成分墨水的混合物:青色、品红色、黄色和黑色。 这些用于表示颜色的其他一些不同技术称为颜色空间。 一些最常用的色彩空间是 RGB、CMYK、HSL 和 HSV。 CMYK 主要用于印刷行业,而其他所有用于数字媒体。

RGB色彩空间

任何传输光的物理电子媒体(如 CRT 屏幕、LCD 或电话)都使用三种成分产生颜色:红、绿、蓝。 人眼可以通过刺激眼睛中的三种颜色受体来检测数百万种颜色。 您可以将这些受体与 R、G 和 B 联系起来。

理想情况下,每个颜色分量都存储在一个字节中,其值可以在 0 到 255 之间。

HSL 和 HSV 颜色空间

在立方体上安排 RGB 颜色空间是相当具有挑战性的。 尝试在立方体和/或色轮上表示它的结果很差。 在处理百万或颜色时,每种颜色都无法在 RGB 颜色空间上正确对齐。

红宝石图片搜索引擎

为了克服这个问题,研究人员在 1970 年代引入了 HSV(色相、饱和度、值)和 HSL(色相、饱和度、亮度)色彩空间。 这两种颜色空间都可以在色轮上正确对齐,从而更容易识别其上的各种色调。

颜色的红宝石

Camalian是关于颜色的。 使用 Camalian 可以做的最简单的事情之一就是识别图像中使用的每种颜色。

假设我们有一张有 15 种不同颜色的图像。

从样本中识别颜色肯定比从图像本身识别颜色更容易。 此外,这是一张简单的图像,而拍摄的真实照片在其调色板方面往往更加多样化。 从图像中提取颜色值需要一些非常棘手的代码,这就是 Camalian 的用武之地。它为您完成了这些棘手的事情,以便您可以轻松地从图像中提取与颜色相关的信息。

入门

如果使用 Camalian 提取颜色很容易,那么使用它进行安装就更容易了。 您可以通过执行以下命令安装 Ruby gem:

 gem install camalian

要使用这个 gem,你可以直接在你的 Ruby 脚本中使用它:

 require 'camalian'

提取颜色

要从图像中提取颜色,我们首先需要将其加载到内存中,并使用图像对象上的方法:

 image = Camalian::load( File.join( File.dirname(__FILE__), 'colormap.png') ) colors = image.prominent_colors(15) puts colors.map(&:to_hex)

这段代码从脚本所在的目录加载一个名为“colormap.png”的图像,并从中提取 15 种最突出的颜色。

要运行它,将文件保存为“color_test1.rb”并通过ruby color_test1.rb在 shell 中运行它。 它应该产生类似于以下内容的输出:

 ["#318578", "#41b53f", "#2560a3", "#359169", "#2154b1", "#4dda15", "#1d48bf", "#1530dc", "#296d94", "#193dcd", "#3da94d", "#45c131", "#3da84e", "#2d7986", "#193cce"]

就这么简单! 我们刚刚提取了上图中使用的 15 种颜色。 你能想象用循环代码,或者更糟的是,用你的眼睛来做这件事吗? 让我们把事情提高一个档次。 这一次,我们将尝试在具有更多细节的图像上使用 Camalian:

通过在此图像上运行相同的脚本,我们得到以下信息:

 [“#210b03”, “#723209”, “#974d09”, “#ae5d08”, “#c77414”, “#d77f15”, “#ffea54”, “#94651f”, “#b66a15”, “#c25f06”, “#fdd94d”, “#d39a39”, “#efa540”, “#fffffe”, “#fff655”]

尝试可视化上面生成的颜色值数组会给我们这样的结果:

调色板很好,但提取的颜色没有特定的图案。 让我们按相似度对颜色值进行排序,看看是否有帮助。 我们需要做的就是在将数组实际打印到控制台之前再调用一个函数:

 colors = image.prominent_colors(15).sort_similar_colors 

但是如果我们想提取相对较浅的颜色呢? 可能我们想要只有 40% 暗的颜色,或者换句话说,亮度(在 HSL 颜色空间中)值在 0 到 40 之间。我们需要做的就是:

 colors = image.prominent_colors(15).light_colors(0, 40) 

制作图片搜索引擎

现在我们知道使用 Camalian 处理颜色是多么容易,让我们构建一个简单的 Web 应用程序,它允许您上传图像并让它们按颜色索引。 为简洁起见,我们将跳过构建 Ruby 应用程序所涉及的各种细节。 相反,我们将专注于处理颜色和 Camalian 用法的细节。

至于这个 Ruby 应用程序的范围,我们将其限制为处理图像上传,在存储图像之前从图像中提取颜色,以及根据选择的颜色和阈值搜索上传的图像。

下面是一个模型图来解释我们的应用程序的结构:

上传的每个图像都使用 PortfolioItem 对象表示。 每个 Color 对象代表通过上传的图像发现的独特颜色,最后 PortfolioColor 代表每个图像和其中找到的颜色之间的关系。

应用程序的大多数部分都是非常标准的,尤其是在处理图像上传、创建模型实体并将它们持久化到数据库等方面。如果您是 Ruby 开发人员,这些应该是不费吹灰之力的。 以下是用于从上传的图像中提取颜色的方法:

 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

这有助于提取调色板并保存到数据库。 请注意我们如何仅提取特定数量的突出颜色(用户可以在上传图像时定义)。

当用户通过 Web UI 上的表单提交图像时,图像会通过 post 处理程序接收,并为其创建一个新的PortfolioItem 。 每当投资组合项目持久保存到数据库时,都会调用此方法extract_colors

为了能够在页面上渲染调色板,我们使用了一个简单的助手:

 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

它本质上创建了一个具有小正方形 span 的div ,每个跨度的背景颜色都设置为提取的突出颜色之一。

最后,要实现搜索,我们必须使用一些数学和逻辑:

 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

使用colors_by_hsl方法,我们可以获取与我们的查询匹配的所有Color实体。 并且,通过这些我们可以识别所有上传的图像并呈现我们的搜索结果页面。 查询本身相当简单。 给定一个特定的颜色和一个相似值,为每个颜色分量计算一个值范围。

这几乎是所有困难的部分。

试一试

你可以在 GitHub 上找到完整的代码。 您可以将此应用程序的实例部署到 Heroku,或在本地试用:

 git clone https://github.com/nazarhussain/camalian-sample-app.git cd camalian-sample-app bundle install rake db:migrate rails s

最终的应用程序看起来像这样:

应用程序运行后,将 Web 浏览器指向 http://localhost:3000。 使用屏幕上的表格,上传一些不同调色板的图像。 然后,要按颜色搜索图像,请使用右上角的搜索字段。 阈值下拉菜单允许您指定匹配图像颜色的差异容差。

下一步是什么?

我们在本文中构建的演示应用程序相当简单,但可能性是无穷无尽的! 该库的其他一些实际用途包括:

  • 限制用户上传深色头像
  • 使网站的颜色主题适应用户上传的一些图片
  • 对于设计竞赛,根据调色板要求自动验证提交

您可以在 GitHub 上进一步探索该库并查看其源代码。 随意通过创建问题来报告错误或通过发送拉取请求来贡献。