実は非常に沢山の方法があります。
- chunky_png および oily_png
- mini_magick および rmagick
- magro
などがあります。その他opencvバインディングを使ったり、Ruby/Tkを使ったり、pnmを経由したりする方法もあります。
ここではRuby-Gnomeの一部である、GdkPixbufを使います。
https://github.com/ruby-gnome2/ruby-gnome2
gem install gtk3
Gtkは↓みたいなGUIを作るやつだけど、今回はそれに関しては最後の方にちょこっとだけ出てきます。
画像素材には、かわいいQiitanを使います1。
そうそう、私の落書きしたぶさかわのQiitanっぽい動物の画像もありますので欲しい人はどうぞ(一体何の記事なんだ
画像→ピクセル
require 'gdk_pixbuf2'
image = GdkPixbuf::Pixbuf.new(file: "qiitan-icon.png")
width = image.width
height = image.height
data = image.pixels
はい、Qiitanが、数字の切れ端になってしまいましたね。
画像 → ピクセル(行列)
このまま、Rubyの配列のまま頑張ってもいいのですが、それだと辛いので、ここでは専用の行列計算ライブラリを使いしょう。Numo::NArrayです。
https://github.com/ruby-numo/numo-narray
わざわざRubyの配列を経由しなくても、バイナリ文字列を経由すれば大丈夫ですね。
require 'gdk_pixbuf2'
require 'numo/narray'
image = GdkPixbuf::Pixbuf.new(file: "qiitan-icon.png")
width = image.width
height = image.height
data = image.pixel_bytes.to_str
narray = Numo::UInt8.from_binary data
narray.reshape!(height, width, 4)
さいごのreshape
の4に注目してください。Qiitanは透過性の情報が入っているので、チャンネルが4つあります。
ピクセル(行列) → 画像
逆に行列からQiitan画像を復元してみましょう。
require 'gdk_pixbuf2'
data2 = narray.to_string
image2 = GdkPixbuf::Pixbuf.new(data: data2, width: width, height: height, has_alpha: true)
image2.save("qiitan-reloaded.png")
無事、Qiitanが復活しました!
このQiitanの画像は4チャンネルなので、has_alpha
に気をつけてください。
フィルターでもかけてみるか
さて、これでこの記事は終わりでもいいのですが、せっかくなので適当にフィルタをかけてみましょう。
require 'gdk_pixbuf2'
require 'numo/narray'
# フィルターをかける関数
def filter(na, filter)
height, width, _c = na.shape
f_height, f_width = filter.shape
na2 = Numo::UInt8.zeros(height + f_height - 1, width + f_width - 1)
x = (f_height - 1) / 2
y = (f_width - 1) / 2
na2[x...-x, y...-y] = na
na3 = na.clone.fill 0
f_height.times do |i|
f_width.times do |j|
temp = (na2[i..(i - f_height), j..(j - f_width)] * filter[i, j])
na3 += temp
end
end
Numo::UInt8.cast(na3.ceil)
end
image = GdkPixbuf::Pixbuf.new(file: "qiitan-icon.png")
width = image.width
height = image.height
data = image.pixel_bytes.to_str
narray = Numo::UInt8.from_binary data
narray.reshape!(height, width, 4)
# 横ブレを引き起こすフィルタ
myfilter = Numo::SFloat.ones(3,51)
myfilter /= myfilter.sum
# アルファチャンネル以外にフィルターをかける
3.times do |i|
narray[true, true, i] = filter(narray[true, true, i], myfilter)
end
data2 = narray.to_string
image2 = GdkPixbuf::Pixbuf.new(data: data2, width: width, height: height, has_alpha: true)
image2.save("qiitan-vibrated.png")
フィルタを変えてみましょう
myfilter = Numo::SFloat[[0,-1,0],[-1,0,1],[0,1,0]]
なかなかいい感じですね。
ほかにもチャンネルの順番を入れ替えるだけで、簡単にQiitanの色違いができたりします。
ウィンドウに表示してみよう
さらにおまけで、これらの画像をWindowに表示することを考えます。GdkPixbufを使っているのでGTKを使えば簡単ですね。
ソースコードは適当ですが、こんな感じでしょうか
require 'gtk3'
win = Gtk::Window.new
win.signal_connect(:destroy) { Gtk.main_quit }
hbox = Gtk::Box.new(:horizontal)
win.add hbox
Dir.glob('*.png').each do |file|
pixbuf = GdkPixbuf::Pixbuf.new file: file
pixbuf = pixbuf.scale(100, 100)
image = Gtk::Image.new(pixbuf: pixbuf)
hbox.add image
end
win.show_all
Gtk.main
この記事は以上です。
-
著作権ありそうだけど、たぶんこのサイト内で使うぶんには怒られないであろう… ↩