実は非常に沢山の方法があります。
- 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
この記事は以上です。
-
著作権ありそうだけど、たぶんこのサイト内で使うぶんには怒られないであろう… ↩





![qiitan-[0, 1, 2, 3].png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F144608%2Fbde425d5-9e78-4b32-4e78-d6d6c5e31bc3.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f8c94b27162e0233d67e923fc7d04d99)
![qiitan-[0, 2, 1, 3].png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F144608%2F60f0f0c0-8adc-6e48-1aca-f3053627d5fa.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=d0e3e2f6b5848977500d2cacedb69400)
![qiitan-[1, 0, 2, 3].png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F144608%2Fc586cb49-b5d4-2650-40d7-4eba7b365f4b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=af3f26256dc3afa6febd52f213abdab8)
![qiitan-[2, 1, 0, 3].png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F144608%2F59e51e73-c9a6-0689-6be4-d6edd68b43d4.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ef8893816ec89c173e387295590f3941)
