魔術師見習いのノート

プロフィール

魔術師見習い
Author魔術師見習い-_-.
Twitter魔術師見習い

コンピュータ関係のメモを主に書きます.

MENU

Ruby GTK+2 画像処理

投稿日:
修正日:
タグ:

本稿はRuby GTK+2の画像関係に関するメモである.

Ruby GTK+2で単に画像を使用する際Gtk::Imageクラスを使用する.

#!/usr/bin/ruby1.8

require 'gtk2'

win = Gtk::Window.new()
img = Gtk::Image.new("./img.png")
win.add(img)
win.show_all()
Gtk.main()
この時,表示される画像のサイズはオリジナルのサイズで使用される.しかし画像があまりにも大きい場合など,そのような表示が好ましくないことがある.そのような場合,次のようなの解決策がある.
  • スクロールバーを付けて表示される範囲を制限
  • 画像のサイズを変更
前者の場合,Gtk::ScrolledWindowを使用して解決できる.後者の場合,今から紹介する方法がある.

画像サイズ変更

次のコードは画像のサイズを倍にしたものを表示するプログラムのものである.

#!/usr/bin/ruby1.8

require 'gtk2'

win = Gtk::Window.new()
img = Gtk::Image.new()
src_pxb = Gdk::Pixbuf.new("./img.png") dst_pxb = src_pxb.scale(src_pxb.width*2, src_pxb.height*2, Gdk::Pixbuf::INTERP_HYPER) img.set_pixbuf(dst_pxb)
win.add(img) win.show_all() Gtk.main()
画像のサイズを変更する場合,Gdk::Pixbufを使用する.GtkではなくGdkであることに注意.scale関数など,Pixbufに関する詳細はここを参考にされたし.

scaleの第1引数は新しい画像の横幅,第2引数は新しい画像の高さ,第3引数は画像サイズの変更に伴う補完の方法を表す.第3引数はGdkInterpType型で,標準ではGdk::Pixbuf::INTERP_BILINEARである.GdkInterpType型の詳細はここを参照されたし.

画像合成

GTK+では2.8以降cairoを用いてwidgetの描画を行なっている.Ruby GTK2ではこれを使用することで複数の画像を合成することができる. 以下にサンプルコードを示す.

bg.png

img.png
#!/usr/bin/ruby1.8
# -*- coding: utf-8 -*-

require 'gtk2'
require 'cairo'

win = Gtk::Window.new()
img = Gtk::Image.new()

bg = Gdk::Pixbuf.new("./bg.png")
pict0 = Gdk::Pixbuf.new("./img.png")
pict1 = Gdk::Pixbuf.new("./img.png")
pxm = Gdk::Pixmap.new(nil, bg.width, bg.height, 24) ctx = pxm.create_cairo_context # bg ctx.set_source_pixbuf(bg) ctx.paint # pict0 ctx.set_source_pixbuf(pict0, -20, -25) ctx.paint # pict1 ctx.set_source_pixbuf(pict1, 0, -5) ctx.paint pxb = Gdk::Pixbuf.from_drawable(Gdk::Colormap.system, pxm, 0, 0, bg.width, bg.height)
img.set_pixbuf(pxb) win.add(img) win.show_all() Gtk.main()
合成した画像(Gdk::Pixbuf)はsave関数を使用することで保存することもできる.
pxb.save("output.png", "png")

透明化

paint関数は引数で透明度を指定できる. 以下はそのサンプルである.

#!/usr/bin/ruby1.8
	
require 'gtk2'
require 'cairo'
	
win = Gtk::Window.new()
img = Gtk::Image.new()
bg = Gdk::Pixbuf.new("./bg.png")
pict = Gdk::Pixbuf.new("./img.png")
pxm = Gdk::Pixmap.new(nil, bg.width, bg.height, 24)
ctx = pxm.create_cairo_context
	
ctx.set_source_pixbuf(bg, 0, 0)
ctx.paint()
	
ctx.set_source_pixbuf(pict, 0, 0)
ctx.paint(0.7)
	
pxb = Gdk::Pixbuf.from_drawable(Gdk::Colormap.system, pxm, 0, 0, pict.width, pict.height)
img.set_pixbuf(pxb)
	
win.add(img)
win.show_all()
Gtk.main()
与える引数が1に近いほど透明度が低く,0に近いほど透明度が高い.

gimpで背景を透明に

ここでは既存の画像の背景を透明にする方法としてGIMPを使った例を紹介する.本稿で使用するGIMPのバージョンは2.8.2である.透明化の流れは次のような手順で行う.

  1. 透明化の設定
    1. [レイヤー(L)]
    2. [透明部分(A)]
    3. [アルファチャンネルを追加(H)]
  2. 背景の切り取り
背景の切り取りには選択を使用するGIMPの選択には矩形選択,楕円選択,自由選択,ファジー選択などがある.最初の3つの選択の機能は言葉から想像できるとして,ファジー選択についてだけ紹介する.ファジー選択ではクリックした位置のピクセル近似色領域を判断して選択領域が決定する.それゆえ,単色の背景などの場合これで選択して切り取りを行えば,比較的簡単に背景を透明にできるだろう.

こちらのサイトを参考に.

参考サイト

Ruby便利機能 メモ

投稿日:
更新日:
タグ:

Rubyの便利なメソッドをメモ。 なお後述の実行結果は見やすいように形成している。

配列

  • product
  • combination
  • reject
  • select
  • collect/map
  • transpose
  • zip
  • each
  • each_with_index
  • join
  • uniq
  • split
  • inject
配列1.product(配列2, ...)
複数の配列から組み合わせて、順列を作成する。
ary1 = [1, 2, 3]
ary2 = ["a", "b"]
p ary1.product(ary2)
実行結果
[[1, "a"], [1, "b"],
 [2, "a"], [2, "b"],
 [3, "a"], [3, "b"]]
productは3つ以上の配列でも良い。
ary1 = [1, 2, 3]
ary2 = ["a", "b"]
ary3 = [-10, -20]
p ary1.product(ary2, ary3)
実行結果
[[1, "a", -10], [1, "a", -20],
 [1, "b", -10], [1, "b", -20],
 [2, "a", -10], [2, "a", -20],
 [2, "b", -10], [2, "b", -20],
 [3, "a", -10], [3, "a", -20],
 [3, "b", -10], [3, "b", -20]]
配列.combination(n){|要素, ...| block }
配列から組み合わせを生成する。要素はn個記述する。
[1, 2, 3].combination(2){|e1,e2|
  puts "#{e1} #{e2}"
}
実行結果
1 2
1 3
2 3
配列.reject{|要素| 削除条件}
配列から特定の要素を削除した配列を生成する。
奇数のみ
p [1, 2, 3, 4, 5, 6].reject{|x| x%2==0 }
実行結果
[1, 3, 5]
配列.select{|要素| 選択条件}
配列から特定の要素を選択した配列を生成する。
奇数のみ
p [1, 2, 3, 4, 5, 6].select{|x| x%2==1 }
実行結果
[1, 3, 5]
配列.collect{|要素| 新しい要素}
配列.map{|要素| 新しい要素}
各要素に対して何らかの処理を行い、新たな配列を生成する。
全ての要素を倍に
p [1, 2, 3, 4, 5, 6].collect{|x| x*2 }
実行結果
[2, 4, 6, 8, 10, 12]
配列.transpose()
配列を行列とみなし、行と列を交換する。
irb(main):002:0> [[1,2,3], [4,5,6]].transpose()
=> [[1, 4], [2, 5], [3, 6]]
配列.zip(other_array, ...)
配列の要素を引数として、other_arrayと組み合わせ、配列の配列を作成 する。
irb(main):002:0> [1,2,3].zip([4,5,6])
=> [[1, 4], [2, 5], [3, 6]]
irb(main):001:0> [1,2,3].zip([4,5,6],[7,8,9])
=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
irb(main):004:0> [1,2,3].zip([4,5,6],[7,8,9]).each{|x,y,z| puts
"#{x}-#{y}-#{z}" }
1-4-7
2-5-8
3-6-9
=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
配列.each{|要素| block}
各要素に対して何らかの処理を行う。
全ての要素を倍に
[1, 2, 3].each{|x| puts x*2 }
実行結果
2
4
6
配列.each_with_index{|要素, 添字番号| block}
eachに加えて、添字番号も取得する。
全ての要素を倍に
["hoge", "fuga", "piyo"].each{|v, i| puts "#{i} #{v}" }
実行結果
0 hoge
1 fuga
2 piyo
配列.join(区切り記号の文字列)
配列を結合して文字列を生成する。
2次元配列をCSV形式で出力
ary1 = [1, 2, 3]
ary2 = ["a", "b"]
ary3 = [-10, -20]
p ary1.product(ary2, ary3).each{|x|
 puts x.join(",")
}
実行結果
1,a,-10
1,a,-20
1,b,-10
1,b,-20
2,a,-10
2,a,-20
2,b,-10
2,b,-20
3,a,-10
3,a,-20
3,b,-10
3,b,-20
配列.uniq
配列から重複を削除する。
2次元配列をCSV形式で出力
ary1 = [1, 1, 2]
ary2 = [3, 4]
p ary1.product(ary2)
puts
p ary1.product(ary2).uniq
実行結果
[[1, 3], [1, 4],
 [1, 3], [1, 4], [2, 3], [2, 4]]

[[1, 3], [1, 4], [2, 3], [2, 4]]
文字列.split(区切り記号)
文字列を指定した文字列で区切って、配列を生成する。
  • irb(main):001:0> "hoge,fuga,piyo,,foo".split(/,+/)
    => ["hoge", "fuga", "piyo", "foo"]
    
  • irb(main):002:0> "hoge,fuga,piyo,,foo".split(",")
    => ["hoge", "fuga", "piyo", "", "foo"]
    
配列.inect{|合計, 要素| 合計}
配列.inect(初期値){|合計, 要素| 合計}
配列.inect(初期値, 合算関数)
配列.inect(合算関数)
各要素を順次処理して結果を合算していく。配列と書いたが実際にはEnumerable#inject。
ブロックの中で指定した結果の合計が次の周期の引数の合計になり、最終的な合計が返却値となる。
初期値を指定しない場合は配列の1つ目の要素が初期値で、2つ目の要素から始まる。
例1)
[1,2,3,4,5].inject{|s,v| p s; s+v}
実行結果
1
3
6
10
=> 15
例2)
[1,2,3,4,5].inject(0){|s,v| p s; s+v}
実行結果
0
1
3
6
10
=> 15
配列の合計や乗算などを扱う場合に便利。
例えば3~5(3,4,5)の整数の乗算結果は以下にように記載できる。
(3..5).inject(:*)

ハッシュ

ハッシュ.invert()
ハッシュのキーと値を入れ替える。
irb(main):001:0> h = {"a"=>1, "b"=>>, "c"=>3}
=> {"a"=>1, "b"=>2, "c"=>3}
irb(main):002:0> h.invert
=> {1=>"a", 2=>"b", 3=>"c"}
irb(main):003:0>

IO, File

IO.read(size)
IOからサイズ分文字列を読み込む。size省略で全て読み込む。
IO.readlines()
IOから全て読み込んで、行毎に配列にする。
IO.popen(コマンド, モード){|IO| 処理}
irb(main):001:0> IO.popen("date"){|io| puts io.read}
2016年  3月  5日 土曜日 15:30:54 JST
=> nil

String

文字列.scan(正規表現)
正規表現に一致する文字列の配列を返す。
irb(main):001:0> STDIN.read.scan(/[^ ]藤/)
さ藤 佐藤 田中 藤野 斎藤
佐々木 遠藤 鈴木
=> ["さ藤", "佐藤", "斎藤", "遠藤"]
irb(main):002:0>
sprintf(str, arg, ...)
str % arg
書式付き文字列を作成する。
  • puts "%.2d" % 2
  • puts "%.2d %s" % [2, "hoge"]
  • puts sprintf "%.2d %s", 2, "hoge"

メタプログラミングRuby まとめ

投稿日:
タグ:

Ruby

本稿は『メタプログラミングRuby』を読んだ自分用のまとめ。

Rubyの基本メソッド
BasicObject#ancestors
継承関係
メソッドを呼び出す場合,ここで取得する配列の0から順番に検索され,使用される。 例えば,「"hoge".func」のようなメソッドを呼んだ場合,Stringから順に検索される。
[String, Comparable, Object, Kernel, BasicObject]
Kernel#p
オブジェクトの中身を形成(Object#inspectメソッドを呼び出す)して出力する
BasicObject#methods
クラスで定義されたメソッド一覧
public_methods
protected_methods
private_methods
singleton_methods
public・protected・private・特異メソッド一覧
BasicObject#instance_varitables
オブジェクトが持つインスタンス変数一覧
Object#class
クラスを取得
Class#superclass
スーパクラスを取得
オブジェクトモデル
オープンクラス

Rubyでは,定義されたクラスを開いて,内容を変更することができる。

p String.methods.include?(:to_num) # false

class String
  def to_num()
    self.to_i # ※ Rubyのメソッド定義ではreturnがない場合,最後に評価した値を返す
  end
end

a = "123"
p a.to_num # 123

ちなみに既に定義されたメソッドがある場合,上書きする(モンキーパッチ)。

class Hoge
  def func()
    puts "hello"
  end
end

a = Hoge.new

class Hoge
  def func()
    puts "bye"
  end
end

a.func() # byeを出力

Refinements(Ruby2.0以降)
usingを使用すると,引数で指定したモジュールで定義された拡張を現在のクラス、モジュールで有効にできる。
module RefStr
  refine String do
    def to_num()
      self.to_i
    end
  end
end


using RefStr

"123".to_num
メソッド
動的ディスパッチ
Object#send(name [, args, ...])
実行時に呼び出すメソッドを動的に変更する。
class Hoge
  def func1()
    puts "hello"
  end
  def func2(str)
    puts "#{str}"
  end
  def func3()
    puts "bye"
  end
end

a = Hoge.new
a.send(:func1)
a.send(:func2, "yeah!")  
a.send("func3".to_sym)   # 文字列 -> シンボル
動的メソッド
Module#define_method(methodname){|arg, ..| block }
メソッドを動的に定義する。
define_method(:func1){|x| puts x}

class Hoge
  define_method(:func2){|x| puts x}
end

func1("hello")
Hoge.new.func2("bye!")
Object#method_missing(methodname, [*args, &block])
メソッドが見つからない場合,method_missingが呼び出される。
class Hoge
  def method_missing(methodname)
    puts "No #{methodname}!"
  end
end

a = Hoge.new
a.func # No func!
ゴーストメソッド
def method_missing(methodname, *args, &blcok)
  if methodname==:func1 then
    puts "#{args}" 
  else
    super.method_missing(methodname, args, block)
  end
end

func1 1 # [1]
func2 2 # ↓
# NoMethodError: undefined method `func2' for main:Object
#       from (irb):8:in `method_missing'
#       from (irb):13
#       from /usr/bin/irb:12:in `<main>'
その他
可変長引数(*v)
呼び出される側
func(*v)
  p v
end
irb(main):002:0> func(1,2,3)
[1, 2, 3]
=> [1, 2, 3]
呼び出す側
def func(a,b)
   puts "a=#{a}, b=#{b}"
end
irb(main):015:0> args = [1,2].product(["a", "b"])
=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
irb(main):016:0> args.each{|a| func(*a)}
a=1, b=a
a=1, b=b
a=2, b=a
a=2, b=b
=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
irb(main):017:0>

一覧