fullseye

照合と位置合わせ(特徴点・頑健推定・強度による位置合わせと、真値による精度の地図) — 使い方ガイド

この族は何をする道具箱か

2 枚の画像の「どこがどこに対応するか」を決める 12 op / 2 カテゴリ(numpy + scipy のみ。台帳は opsmatchreg.py、実体は featmatch.py と intensityreg.py):

太字の 3 本が、物理・撮像のシミュレーションで真値を作れる Fullseye だけの層です。標準のアルゴリズムの 上に「この条件ならどこまで信頼できるか」を数で載せます。

なぜ足したか —— 在庫を数えた結果(2026-11-03)

監査(completeness_audit_2026_11_03)で、スケール不変の特徴と照合は OpenCV・scikit-image・kornia の 全部にあるのに Fullseye は数を数えるだけ(xcv_orb_count)、強度による位置合わせは ECC・MI 目的の アフィン・B-spline FFD が無い、と出ました。

既にあったもの 何をするか この族との関係
features.harris_corners / fast_corners / match_descriptors Harris・FAST + 正規化パッチ + 比の検定 並存。features_orb は fast_corners を再利用。features_match の実数の規則は match_descriptors と同じ組を返す(テストで固定)。パッチ記述子は回転・拡大に不変でない(部品の PoC で 37 度回転の 4 隅誤差 158 px)
mosaic.proj_match_points_ransac (row, col)・正規化なし DLT・固定回数の RANSAC 並存。座標の順が逆なので混ぜない。ransac_homography は正規化 DLT(twoview._homography_dlt)を再利用
mosaic.gen_projective_mosaic H で貼り合わせ そのまま使う(H の向きが同じ (x, y))。新しい貼り合わせ op は作らない
reconstruction.match_fundamental_matrix_ransac / twoview.fundamental_8point F の推定 ransac_fundamental は fundamental_8point を再利用
aoi.register_image 位相相関の並進 + 当てになるかの量 精度の地図・収束の範囲の比べる相手(method="phase")
deformreg.demons_register demons の密な場 比べる相手(method="demons")。場の向きを揃えたので画素ごとにそのまま比べられる
imgmetrics.mutual_information MI の指標 指標。register_mi の目的関数は振り分けつきの別の推定量
deform_ffd B-spline の変形を掛ける(拡張) 推定はしない。register_ffd が推定

新しい型語彙は 1 つも作っていません(image2d / table / flow2d)。表は kind の印つきで、 違う kind を渡すと ValueError です。

使う順序

flowchart TD
    A["画像 2 枚"] --> Q{"明るさの対応は?"}
    Q -- "同じ撮像・模様が多い・大きくずれる" --> F["features_sift / features_orb"]
    F --> M["features_match"]
    M --> R["ransac_homography<br/>(model は物理に合わせる)"]
    R --> ECC["register_ecc で詰める<br/>(warp_init = R の H)"]
    Q -- "同じ撮像・ずれが小さい" --> ECC
    Q -- "撮像が違う(可視と赤外など)" --> MI["register_mi"]
    Q -- "局所に曲がる(シート・生体)" --> FFD["register_ffd / demons_register"]
    ECC --> FL["registration_to_flow2d"]
    MI --> FL
    FFD --> FL
    FL --> PIV["piv_flow_magnitude / piv_vorticity"]
    S["シミュレーションの真値"] --> ACC["features_accuracy_table<br/>register_accuracy_map<br/>register_capture_range"]

最小の例

import numpy as np
import featmatch, intensityreg

img = featmatch.synthetic_scene((128, 128), seed=3)
H = featmatch.condition_homography(img.shape, rotate_deg=30.0, scale=0.8)   # 真値
img2, _ = featmatch.warp_homography(img, H)
m = featmatch.features_match(featmatch.features_sift(img), featmatch.features_sift(img2))
h = featmatch.ransac_homography(m)
print("4 隅誤差 %.3f px" % featmatch.corner_error(h["H"], H, img.shape))

g = intensityreg.smooth_test_field(img.shape, amplitude=2.0, wavelength=48)  # 真の変形場
acc = intensityreg.register_accuracy_map(img, g, method="ffd")
print("FFD の EPE 平均 %.3f px / 95%% 点 %.3f px" % (acc["epe_mean"], acc["epe_p95"]))

符号の約束(ここを間違えると黙って逆を向く)

測ってわかったこと(数は PoC とテストが固定)

限界(正直に)