SciPyには多くの窓関数が用意されています。有名なハミングの窓から何やら聞いたことのない窓まで。ただすべて1次元です。多分。
画像を扱うときには2次元で扱うことが多いのですが、どうにも2次元の窓関数が見当たらないのでSciPyで用意されている任意の窓関数を2次元に拡張する方法を考えてみます。
2次元ハニングの窓
2次元のハニングの窓を真面目にコーディングしてみます。中心からの距離に対して窓をかけるようなイメージです。
1 2 3 4 5 6 7 8 9 10 | ### # sizeは窓の1辺の長さ。奇数 def han2d(size): tap = size / / 2 rad = np.linspace( - np.pi, np.pi, 2 * tap + 1 ) # -pi ~ pi radx, rady = np.meshgrid(rad, rad) radgrid = np.sqrt(radx * * 2 + rady * * 2 ) # 原点からの距離を示したグリッド radgrid[radgrid > np.pi] = np.pi # piより大きいものはpiにクリップ(四隅) han2 = 0.5 + 0.5 * np.cos(radgrid) # ハン窓の定義 |
これでなんとなく正しそうなハニングの窓(ハン窓)ができたような気がします。
ただこの要領で種類を増やそうと思うと、すべての窓関数の定義式を一つ一つトレースしないといけません。とてもめんどくさいです。
手を抜きます。
任意の窓関数の2次元化
1次元の窓関数をSciPyに作ってもらって、それを補間で2次元化します。中心からの距離を2次元のマップにして既存の1次元配列を参照すれば良いです。
1 2 3 4 5 6 7 8 9 10 11 12 13 | size = 21 tap = size * 2 # 元となる1次元配列は欲しいサイズの倍で作る han = signal.hann(tap * 2 + 1 ) # 1次元のハン窓 han = han[tap:tap * 2 ] # 後ろの半周期取り出し x = np.linspace( - tap, tap, size) xx,yy = np.meshgrid(x, x) mapx = np.sqrt(xx * * 2 + yy * * 2 ).astype(np.float32) mapx[mapx > tap] = tap # 中心からの距離マップ mapy = np.zeros_like(mapx) # 全マップ0 out = cv2.remap(han, mapy, mapx, cv2.INTER_LINEAR ) |
これでよいです。4行目の関数を好きな窓関数に差し替えれば、好きな2次元の窓関数が作れます。
mapxには中心からの距離が格納されているので、こんな感じです。ど真ん中が0で外に向かって中心からの距離にあたる値が入っています。

このマップの値に応じて、1次元のハン窓から線形補間して2次元化しているので、誤差はでます。

グラフからはわかりませんけどね。
先ほどの真面目に作った2次元ハン窓の関数との誤差を計算してみます
1 2 | han2 = han2d(size) maximum = np. max (np. abs (han2 - out)) |
この計算による最大誤差 0.000618920…です。(真面目に作った窓が正しいと仮定して)まぁ使用用途に寄りますが、一般的には許容範囲でしょう。
バリエーション
ハミングの窓

1 | han = signal.hamming(tap*2+1) |
ブラックマンの窓

1 | han = signal.blackman(tap * 2 + 1 ) |
矩形窓

1 | han = signal.boxcar(tap * 2 + 1 ) |
ガウシアン窓

1 | han = signal.gaussian(tap * 2 + 1 , 15 ) |
好きなものを使うといい。
まとめ
1次元のデータ列さえあれば簡単に2次元化(近似)できる事がわかってしまいました。ややこしい計算はSciPyさんにお任せして、簡単に2次元への拡張されました。用途に応じて好きな窓が選べます。そんなに機会ないけど。
そして改めてOpenCVのremap関数はやはり大層便利な関数だということがわかりました。
remapの引数のbordermodeもREPLICATEが正しいのかなと思って試しましたが誤差変わらず。まぁこんなもんなのかな。
コメント