位相限定相関(Phase Correlation)などフーリエ変換を用いた画像のズレ量推定手法 - Shinichi0713/AlgorithmChallenges GitHub Wiki

課題

位相限定相関(Phase Correlation)などフーリエ変換を用いた画像のズレ量推定手法では、 「シフト量が大きい場合(特にマイナス方向)」に**推定が正しく行えない(ズレ量が全然合わない)**現象が発生することがあります。

主な原因

  1. 周期境界(wrap-around)問題 フーリエ変換は周期的な信号として画像を扱います。 画像を大きくシフトすると、端からはみ出した部分が**反対側から回り込む(wrap-around)**ように見なされます。 そのため、本来のシフト量が画像サイズの半分を超えると、「逆方向の小さいシフト」として誤検出されます。 例:画像幅100ピクセル、シフト量-60ピクセル → +40ピクセルと誤認される
  2. ピーク位置の誤認識 相関ピークが画像の端に近い位置に現れる場合、正しいピークが見つけにくくなり、ノイズや他のピークと区別がつきにくくなります。
  3. 画像の内容やパディングの影響 画像の内容が周期的でない場合や、背景がゼロでパディングしていない場合、シフト後の画像が元画像と大きく異なり、相関値が低下してピークが不明瞭になることがあります。 まとめ フーリエ変換ベースの手法は、シフト量が画像サイズの半分を超えると「周期的」な扱いになるため、正しいズレ量が得られません。 マイナス方向の大きなシフトは、正方向の小さなシフトと同じ結果になるため、特に誤認しやすいです。 根本的な解決には、画像を十分大きくパディングする、あるいは他の手法(特徴点マッチングなど)を組み合わせる必要があります。 対策例 画像をゼロパディングしてサイズを2倍以上に拡張することで、wrap-around誤差を防ぐことができます。 シフト量の物理的な上限を設け、推定値がその範囲外なら補正するロジックを加えることも一つの方法です。

実装

上記対策の実装例を以下に示す。

import numpy as np
import cv2
from scipy.fft import fft2, ifft2

def pad_to_double_size(img):
    """画像を縦横2倍にゼロパディング"""
    h, w = img.shape
    padded = np.zeros((2*h, 2*w), dtype=img.dtype)
    padded[:h, :w] = img
    return padded

def phase_correlation(img1, img2):
    """ゼロパディング+位相限定相関でシフト量推定"""
    # ゼロパディング
    img1_p = pad_to_double_size(img1)
    img2_p = pad_to_double_size(img2)
    
    # フーリエ変換
    F1 = fft2(img1_p)
    F2 = fft2(img2_p)
    
    # 位相限定相関
    R = F1 * np.conj(F2)
    R /= np.abs(R) + 1e-8   # 0割防止
    r = np.abs(ifft2(R))
    
    # ピーク位置
    max_idx = np.unravel_index(np.argmax(r), r.shape)
    shift_y, shift_x = max_idx
    
    # wrap-around補正
    h, w = img1_p.shape
    if shift_y > h // 2:
        shift_y -= h
    if shift_x > w // 2:
        shift_x -= w
    
    # 元画像サイズに換算
    # (元画像の左上にパディングしているので、そのままOK)
    return shift_x, shift_y

# --- 例 ---
# 画像の読み込み(グレースケール)
img1 = cv2.imread('img1.png', 0)
img2 = cv2.imread('img2.png', 0)

shift_x, shift_y = phase_correlation(img1, img2)
print(f"推定シフト量: x={shift_x}, y={shift_y}")