はじめに
畳み込み演算は、そのまま実装すると、データが大きくなると重くなる。一方、フーリエ変換により、畳み込み演算は単純な掛け算に変換される。これを応用して、畳み込み演算したい二つの配列をフーリエ変換し、乗算を行った後に、フーリエ逆変換する高速化手法が一般に利用される。これをNumo::Pocketfftに実装しversion 0.2.0とした。
numo-pocketfft | RubyGems.org | your community gem host
使い方
Numo::Pocketfftはgemコマンドでインストールできる。pocketfftのコードを同梱しているので、外部ライブラリを別でインストールする必要はない。
$ gem install numo-pocketfft
SciPyにあやかって、メソッド名はfftconvolveとした。二つのNumo::NArrayな配列を渡せば、その(離散)畳み込み演算の結果を返す。
irb(main):001:0> require 'numo/pocketfft' => true irb(main):002:0> Numo::Pocketfft.fftconvolve(Numo::DFloat[1, 2, 3], Numo::DFloat[4, 5]) => Numo::DFloat#shape=[4] [4, 13, 22, 15]
画像処理への応用
画像のフィルタリングは、画像とフィルタの畳み込みで実現される。Magroも使って、アイコンに使ってるハンバーグの画像にSobelフィルタをかけて、エッジを抽出してみる。
require 'magro' require 'numo/pocketfft' # Lena画像を読み込む. img = Magro::IO.imread('lena.png') # グレイスケール化する. gray = img.median(axis: 2) # Sobelフィルタを定義する. fx = Numo::DFloat[[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]] fy = Numo::DFloat[[-1, -2, -1], [0, 0, 0], [1, 2, 1]] # 畳み込みによりSobelフィルタをかける. gx = Numo::Pocketfft.fftconvolve(gray, fx) gy = Numo::Pocketfft.fftconvolve(gray, fy) # エッジ画像を得る. g = Numo::NMath.sqrt(gx**2 + gy**2) g[g>255] = 255 g[g<0] = 0 # 畳み込みによりできた画像の端を削る. # ※ # 元の画像サイズは512x512であり, フィルタのサイズが3x3であることから, # 畳み込みにより (512 + 3 - 1) x (512 + 3 - 1) の画像ができる. # 上下左右の端を1ピクセルずつ削り512x512の画像にする. h, w = gray.shape fh, fw = g.shape sy = (fh - h) / 2 sx = (fw - w) / 2 g = Numo::UInt8.cast(g[sy..h, sx..w]) # 画像を保存する. Magro::IO.imsave('lena_fil.png', g)
これを実行すると、以下のような画像が得られる。Sobelフィルタによりエッジが抽出されていることがわかる。
おわりに
Numo::NArrayな配列で畳み込み演算ができるようになった。使い方の例のように、画像のフィルタリングも簡単になったので、Magroの開発も進むかな?