RubyでRubyを描く

[RubyでRubyを描く // Speaker Deck](https://speakerdeck.com/ash1day/rubyderubywomiao-ku)
RubyKaigi2016最終日の次の日に開催された第74回 Ruby関西 勉強会で発表した内容とその補足になります。

# RubyをRubyで描く とは
一ヶ月ほど前、RubyKaigiを目前にしてテンションが高まりRubyのロゴ
![_ruby.jpeg](https://qiita-image-store.s3.amazonaws.com/0/38375/608c1836-5252-c7b7-4fe1-cf2b5d6acffe.jpeg)
(Copyright © 2006, Yukihiro Matsumoto)

をRubyで描いて信仰心を深めたいという衝動に駆られました。

# 画像を数値化
とりあえずこのRubyのロゴを数値化します。

<img width="533" alt="Screenshot 2016-09-13 02.16.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/2ad43fe4-b5fb-d710-7e9f-f8710336a9ef.png">

ImageMagickのwrapperである[RMagick](https://github.com/rmagick/rmagick)使いました。

```rb
require 'RMagick'
include Magick

# 画像の読み込み
image = ImageList.new('ruby-kit/ruby.jpg')

# ピクセル毎に赤色要素だけを数値化して2次元配列に
z = image.rows.downto(0).map do |y|
  image.columns.times.map do |x|
    image.pixel_color(x, y).red
  end
end
```

元の画像が1000x1000pxぐらいだったので`z`の配列のサイズも1000x1000になっています。

# Heatmap化
とりあえず簡単に画像っぽいものを描くならヒートマップですね。

<img width="571" alt="Screenshot 2016-09-13 02.16.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/e5a27c32-879f-f0b1-4161-c4dc77b5a391.png">
簡単に説明すると、数値を色で表現できる可視化手法で上の例だと値が小さいほど青く、大きいほど赤く表示されます。
Githubのアレです。
<img width="203" alt="Screenshot 2016-09-13 02.19.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/3049a378-1d3a-6d2f-991d-322db4f4c492.png">

今回は僕がSciRuby.jpの活動の一環として書いている[rbplotly](https://github.com/y4ashida/rbplotly)というgemで描画しています。

```rb
require 'rbplotly'

data = [{
  z: z,
  type: :heatmap,
  colorscale: [
    [0.0,  'rgb(  0,   0,   0)'],
    [0.7,  'rgb(256,   0,   0)'],
    [0.95, 'rgb(256, 256, 256)'],
    [1.0,  'rgb(256, 256, 256)']
  ]
}]
layout = { width: 700, height: 700 }

plot = Plotly::Plot.new(data: data, layout: layout)
plot.show # IRuby上以外では#generate_htmlや#download_imageを。
          # 詳しくは https://github.com/ash1day/rbplotly 参照
```

colorscaleを0〜1で指定するところがポイントです。デフォルトでは上の例のように青〜赤なのですが、今回は画像を再現したいので黒〜赤〜白としています。

## 結果と比較
### 結果
<img width="427" alt="Screenshot 2016-09-13 02.27.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/a89dea74-bc80-1b26-1dc5-4330e3482b46.png">

colorscaleを手動でチューニングしたこともあり、結構似ました。

### 比較
<img width="484" alt="Screenshot 2016-09-13 02.27.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/f238bbbb-3f5b-3979-3b36-2e3bcf044dca.png">

左が元の画像です。やっぱりちょっと深みがありますね。

### カラーバリエーション
色の指定を黒〜緑〜白や、黒〜青〜白とすることにより色を変化させることができます。
<img width="617" alt="Screenshot 2016-09-13 02.32.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/b0d234d1-b94b-4e9f-49c4-3b2fdeccc31d.png">


## Heatmapでの問題点
- 色の指定が面倒
- 「それ、画像貼った方がいいんじゃない?」という痛いところを突かれやすい。

# Scatter Plot
というわけで、もっとグラフ描いてる感が出る可視化手法Scatter Plotで描いてみます。何のことはない点プロットです。

<img width="590" alt="Screenshot 2016-09-13 02.36.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/a621b09d-d21c-bd9a-c13a-a80b1490ec21.png">

好きな色を指定できるように今度は先ほど配列化した数値をマーカーの濃度に適用します。

```rb
# opacityの設定が0〜1なので正規化
max = z.flatten.max
min = z.flatten.min
denom = max - min
z.map! { |row| row.map { |red| 1 - (red - min).to_f / denom } }

trace = {
  x: z.length.times.map { z.first.length.times.to_a }.flatten,
  y: z.length.times.map { |y| Array.new(z.first.length - 1, y) }.flatten,
  type: :scatter,
  mode: :markers,
  marker: { size: 1, color: 'rgb(255, 65, 54)', opacity: z.flatten }
}
layout = { width: 700, height: 700 }

plot = Plotly::Plot.new(data: [trace], layout: layout)
plot.show
```

Heatmap版と比べて色の指定が簡単になっているのが分かるでしょうか。これなら何色のRubyでも書けそうですね。

## 結果
<img width="350" alt="Screenshot 2016-09-13 02.51.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/38375/f472323f-4690-f128-5aa3-fa69739f0065.png">

ナウなフラットデザインっぽい色のRubyが描けました。(白い部分も点は打たれていますが、opacityがほぼ0なため見えなくなっています。)