RubyでRubyを描く // Speaker Deck
RubyKaigi2016最終日の次の日に開催された第74回 Ruby関西 勉強会で発表した内容とその補足になります。
RubyをRubyで描く とは
一ヶ月ほど前、RubyKaigiを目前にしてテンションが高まりRubyのロゴ
(Copyright © 2006, Yukihiro Matsumoto)
をRubyで描いて信仰心を深めたいという衝動に駆られました。
画像を数値化
とりあえずこのRubyのロゴを数値化します。
![Screenshot 2016-09-13 02.16.53.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2F2ad43fe4-b5fb-d710-7e9f-f8710336a9ef.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=149ec3fee375befecfead21f3dcc645f)
ImageMagickのwrapperであるRMagick使いました。
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化
とりあえず簡単に画像っぽいものを描くならヒートマップですね。
![Screenshot 2016-09-13 02.16.13.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2Fe5a27c32-879f-f0b1-4161-c4dc77b5a391.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=16b9b759e40d12b7578a2e01718f100e)
![Screenshot 2016-09-13 02.19.56.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2F3049a378-1d3a-6d2f-991d-322db4f4c492.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=d30f13f84a7be6e362edeea27bb8cfca)
今回は僕がSciRuby.jpの活動の一環として書いているrbplotlyというgemで描画しています。
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で指定するところがポイントです。デフォルトでは上の例のように青〜赤なのですが、今回は画像を再現したいので黒〜赤〜白としています。
結果と比較
結果
![Screenshot 2016-09-13 02.27.09.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2Fa89dea74-bc80-1b26-1dc5-4330e3482b46.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=2359e772ef464bea9b3395d66fda1728)
colorscaleを手動でチューニングしたこともあり、結構似ました。
比較
![Screenshot 2016-09-13 02.27.33.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2Ff238bbbb-3f5b-3979-3b36-2e3bcf044dca.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=83e678ac50414781be4ec6d1949c83cd)
左が元の画像です。やっぱりちょっと深みがありますね。
カラーバリエーション
色の指定を黒〜緑〜白や、黒〜青〜白とすることにより色を変化させることができます。
Heatmapでの問題点
- 色の指定が面倒
- 「それ、画像貼った方がいいんじゃない?」という痛いところを突かれやすい。
Scatter Plot
というわけで、もっとグラフ描いてる感が出る可視化手法Scatter Plotで描いてみます。何のことはない点プロットです。
![Screenshot 2016-09-13 02.36.28.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2Fa621b09d-d21c-bd9a-c13a-a80b1490ec21.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f8ad0c7ef5d882c568abb7edefff5892)
好きな色を指定できるように今度は先ほど配列化した数値をマーカーの濃度に適用します。
# 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でも書けそうですね。
結果
![Screenshot 2016-09-13 02.51.16.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F38375%2Ff472323f-4690-f128-5aa3-fa69739f0065.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ecd63eb80dfa220912b571386a55fb70)
ナウなフラットデザインっぽい色のRubyが描けました。(白い部分も点は打たれていますが、opacityがほぼ0なため見えなくなっています。)