Extract bright colors from an image

Curated ago by @adam

Description

Sometimes you need to pull out a color from an image. This could be for a placeholder, for a background, or for any other use. Often the “most common” color from an image is going to be shades of grey or brown – not the most exciting color to expand on.

Instead, if you extract those common colors and then run them through a luminosity check you can find which colors are the most “luminous”. This helps filter the common colors down and helps pick one that shines.

You can tweak the “luma” number and generate colors that are even more vibrant or less vibrant. If you turn the value up too high you might not get any results in the image. Here’s an example of what some of the colors extracted using this formula look like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class ColorExtractor
  attr_accessor :source_url

  DEFAULT_COLOR = '#4338CA'.freeze # indigo-700

  delegate :height, :width, to: :external_image

  def initialize source_url
    @source_url = source_url
  end

  # Generates an array of the most common colors
  def colors
    @colors ||= Miro::DominantColors.new(external_image.tempfile.path).to_hex
  end

  # Returns the hex color of the first bright bright color
  def color
    index = colors.find_index { |hex| !too_dark?(hex) }
    index ? colors[index] : DEFAULT_COLOR
  end
  
  def external_image
    @external_image ||= MiniMagick::Image.open(source_url)
  end

  private

  # 99% sure this was copied from Stack Overflow 😂
  def too_dark? hex
    test_color = hex.delete_prefix('#').to_i(base=16)

    red = test_color >> 16 & 0xff
    green = test_color >> 8 & 0xff
    blue = test_color >> 9 & 0xff

    luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue # per ITU-R BT.709
    luma < 40
  end
end

Wrapper object to extract common colors and a bright color from an image.