色コード「#RRGGBB」をパースして,適当でいいので何色か判断するクラスを作りたい。ちょっとしたアレで使用したいので。

最初に思いついたもの

やっつけなのでろくにテストも書いていない。。

rgbcolor.rb

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
class RGBColor
  def initialize(r, g, b)
    raise ArgumentError unless [r, g, b].all?{|v| (0..255) === v}
    @r = r
    @g = g
    @b = b
    @color_type = detect_color
  end
  attr_accessor :r, :g, :b
 
  def inspect
    "#<RGBColor(#{@r}, #{@g}, #{@b}), @color_type=#{@color_type}>"
  end
 
  def detect_color
    cr = [(@r + 1).to_f / (@g + 1 ), (@g + 1).to_f / (@b + 1 ), (@b + 1).to_f / (@r + 1 )]
    if cr.all?{|v| (0.9..1.1) === v}
      if @r < 52
        color_type = :black
      elsif @r < 188
        color_type = :gray
      else
        color_type = :white
      end
    elsif cr[0] > cr[1]
      if cr[1] > cr[2]
        color_type = :red
      elsif cr[0] > cr[2]
        color_type = :purple
      else
        color_type = :blue
      end
    else
      if cr[0] > cr[2]
        color_type = :yellow
      elsif cr[1] > cr[2]
        color_type = :green
      else
        color_type = :cyan
      end
    end
    color_type
  end
  private :detect_color
 
  def self.parse_hex(str)
    raise ArgumentError unless /^#?([a-fA-F0-9]{6})$/ =~ str
    ccode = $1.to_i(16)
    r = (ccode >> 16) & 255
    g = (ccode >> 8) & 255
    b = ccode & 255
    self.new(r, g, b)
  end
end
 
if __FILE__ == $0
  case ARGV.size
  when 1
    p RGBColor.parse_hex(ARGV[0])
  when 3
    p RGBColor.new(ARGV[0].to_i, ARGV[1].to_i, ARGV[2].to_i)
  else
    STDERR.puts "usage: colorhex.rb (R G B|RRGGBB)"
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
udzura@ubuntu-vaio:~/dev$ irb -rrgbcolor
irb(main):001:0> RGBColor.parse_hex("#aa66cc")
=> #<RGBColor(170, 102, 204), @color_type=:purple>
irb(main):002:0> RGBColor.parse_hex("#dd0000")
=> #<RGBColor(221, 0, 0), @color_type=:red>
irb(main):003:0> RGBColor.parse_hex("#000000")
=> #<RGBColor(0, 0, 0), @color_type=:black>
irb(main):004:0> RGBColor.parse_hex("#888888")
=> #<RGBColor(136, 136, 136), @color_type=:gray>
irb(main):005:0> RGBColor.parse_hex("#00aa88")
=> #<RGBColor(0, 170, 136), @color_type=:cyan>
irb(main):006:0> RGBColor.parse_hex("#00aa44")
=> #<RGBColor(0, 170, 68), @color_type=:cyan>
irb(main):007:0> RGBColor.parse_hex("#00aa00")
=> #<RGBColor(0, 170, 0), @color_type=:green>

大体合ってなくはないけど,何かいいかげん。。。

アルゴリズム

色(r, g, b)(0≦r, g, b≦255)に対して,以下の条件が成り立つなら→で示す色であるとする。

r > g = b → 赤
r < g = b → 水色

g > r = b → 緑
g < r = b → 紫

b > r = g → 青
b < r = g → 黄色

以上を (A) とする。

また,以下を(1),(2),(3)とする。

 r + 1
------- … (1)
 g + 1

 g + 1
------- … (2)
 b + 1

 b + 1
------- … (3)
 r + 1

(A)と(1),(2),(3)から,以下が言える。

(1) > (2) > (3) → 赤
(3) > (2) > (1) → 水色

(2) > (3) > (1) → 緑
(1) > (3) > (2) → 紫

(3) > (1) > (2) → 青
(2) > (1) > (3) → 黄色

また,(1),(2),(3)すべてが1に近い場合は無彩色であろう。なので、rの値で黒,灰色,白を判断する。

これはひどい車輪の再発明

そもそもRubyにはRMagickと言う素晴らしいライブラリが用意されており,それはImageMagickというCUIの画像加工ツールのラッパーである(説明口調)。

色々調べたら,HSLという表現方法のほうが「結局お前何色よ?」が分かりやすくて,RMagickなら Pixcel#to_hlsa とかそういうメソッドで変換できるそうな。どう考えてもこっちの方がいいな……。