OpenCVで学ぶ画像特徴の検出と対応付け — Harris、Shi-Tomasi、FASTの実装と比較

画像中の意味のある構造を自動で抽出・関連付ける技術は、コンピュータビジョンの基盤となる。本稿では、OpenCVを用いた代表的な角点検出アルゴリズム(Harris、Shi-Tomasi、FAST)の原理と実装を、数学的直感と実用性の両面から解説する。

Harris法:自己相関行列に基づく局所変化の評価

Harris検出器は、各画素近傍における輝度変化の方向依存性に着目する。その核となるのは、勾配ベクトルの外積から構成される自己相関行列 M である:

M = Σ [Iₓ², IₓI_y; IₓI_y, I_y²]

ここで Iₓ, I_y はx・y方向の画像勾配。この行列の固有値がともに大きい場合、その位置は「すべての方向で輝度が急変する」角点と判断される。実装上は、行列式とトレースを用いた近似スコア R = det(M) − k·tr(M)² を計算し、閾値処理と非極大抑制により候補を絞り込む。

以下はOpenCVによる再構成コード(パラメータ名を明示的に再命名し、処理フローを可視化可能にした):

import cv2
import numpy as np

def detect_harris_corners(image_path: str, threshold_ratio: float = 0.01):
    src = cv2.imread(image_path)
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY).astype(np.float32)
    
    # Harrisスコアマップ生成(ブロックサイズ=2、Sobelカーネル=3、k=0.04)
    harris_map = cv2.cornerHarris(gray, blockSize=2, ksize=3, k=0.04)
    
    # スコアの正規化と膨張(検出点の視認性向上)
    harris_map = cv2.normalize(harris_map, None, 0, 255, cv2.NORM_MINMAX)
    harris_map = cv2.dilate(harris_map, kernel=np.ones((3,3)))
    
    # 閾値付きマスク作成(最大値のthreshold_ratio倍以上)
    mask = harris_map > (threshold_ratio * harris_map.max())
    
    # 元画像への赤色マーカー描画
    result = src.copy()
    result[mask] = [0, 0, 255]
    
    return result, harris_map

# 使用例
output_img, score_map = detect_harris_corners("scene.jpg")
cv2.imwrite("harris_output.png", output_img)

Shi-Tomasi法:固有値最小値による厳密な角点判定

Shi-TomasiはHarrisの改良版であり、「最も小さい固有値」を直接スコアとして利用する。これは「少なくとも一つの方向で変化が小さい=エッジではない」という物理的直感に基づき、より信頼性の高い角点選択を実現する。OpenCVでは cv2.goodFeaturesToTrack() で実装されており、検出数・品質閾値・最小間隔を制御可能である。

def detect_shitomasi_corners(image_path: str, max_count: int = 30, quality: float = 0.02):
    src = cv2.imread(image_path)
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    
    # Shi-Tomasi検出(固有値最小値ベース)
    corners = cv2.goodFeaturesToTrack(
        gray,
        maxCorners=max_count,
        qualityLevel=quality,
        minDistance=12,
        blockSize=7
    )
    
    # 検出点を円で可視化
    result = src.copy()
    if corners is not None:
        for corner in corners:
            x, y = map(int, corner.ravel())
            cv2.circle(result, (x, y), radius=4, color=(0, 255, 0), thickness=-1)
    
    return result

# 使用例
shitomasi_result = detect_shitomasi_corners("scene.jpg")
cv2.imwrite("shitomasi_output.png", shitomasi_result)

FAST法:16点リングテストによる高速検出

FASTは、中心画素を囲む16点の離散円周上で「連続する12点以上が中心より明るいか暗いか」を判定するシンプルなルールに基づく。計算量が極めて少ないため、リアルタイムアプリケーションに適している。ただし、単独では方向不変性やスケール不変性を持たないため、通常ORBパイプラインの一部として利用される。

def detect_fast_keypoints(image_path: str, suppress_nonmax: bool = True):
    src = cv2.imread(image_path)
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    
    # FAST検出器の初期化
    detector = cv2.FastFeatureDetector_create(
        threshold=20,
        nonmaxSuppression=suppress_nonmax
    )
    
    # キーポイント検出
    keypoints = detector.detect(gray, None)
    
    # 可視化(赤色ドット)
    result = cv2.drawKeypoints(
        src, keypoints, None,
        color=(255, 0, 0),
        flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
    )
    
    return result, len(keypoints)

# NMS有無の比較
with_nms, n_with = detect_fast_keypoints("scene.jpg", suppress_nonmax=True)
without_nms, n_without = detect_fast_keypoints("scene.jpg", suppress_nonmax=False)

print(f"NMS有: {n_with}点, NMS無: {n_without}点")
cv2.imwrite("fast_with_nms.png", with_nms)
cv2.imwrite("fast_without_nms.png", without_nms)

アルゴリズム選択の指針

  • Harris:幾何的整合性が求められるタスク(例:ホモグラフィ推定)で安定。ノイズ耐性は中程度。
  • Shi-Tomasi:検出点の品質優先(例:光流追跡)。Harrisより厳密だが、検出数がやや少ない傾向。
  • FAST:速度重視のリアルタイム系(例:モバイルAR)。後処理(BRIEF記述子+FLANNマッチング)との組み合わせが必須。

タグ: OpenCV computer-vision feature-detection corner-detection Harris

5月26日 05:12 投稿