Pythonを使って2次元の窓関数を作る

python

 SciPyには多くの窓関数が用意されています。有名なハミングの窓から何やら聞いたことのない窓まで。ただすべて1次元です。多分。

 画像を扱うときには2次元で扱うことが多いのですが、どうにも2次元の窓関数が見当たらないのでSciPyで用意されている任意の窓関数を2次元に拡張する方法を考えてみます。

スポンサーリンク

2次元ハニングの窓

 2次元のハニングの窓を真面目にコーディングしてみます。中心からの距離に対して窓をかけるようなイメージです。

###
# 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次元配列を参照すれば良いです。

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次元ハン窓の関数との誤差を計算してみます

han2 = han2d(size)
maximum = np.max(np.abs(han2 - out))

 この計算による最大誤差 0.000618920…です。(真面目に作った窓が正しいと仮定して)まぁ使用用途に寄りますが、一般的には許容範囲でしょう。

スポンサーリンク

バリエーション

ハミングの窓

han = signal.hamming(tap*2+1)

ブラックマンの窓

han = signal.blackman(tap*2+1)

矩形窓

han = signal.boxcar(tap*2+1)

ガウシアン窓

han = signal.gaussian(tap*2+1, 15)

 好きなものを使うといい。

まとめ

 1次元のデータ列さえあれば簡単に2次元化(近似)できる事がわかってしまいました。ややこしい計算はSciPyさんにお任せして、簡単に2次元への拡張されました。用途に応じて好きな窓が選べます。そんなに機会ないけど。

 そして改めてOpenCVのremap関数はやはり大層便利な関数だということがわかりました。

 remapの引数のbordermodeもREPLICATEが正しいのかなと思って試しましたが誤差変わらず。まぁこんなもんなのかな。

pythonメモ
スポンサーリンク
キャンプ工学

コメント

タイトルとURLをコピーしました