🐥
大きな画像から部分画像の座標を取得する方法
概要
大きな画像の一部が切り出された複数の画像から、元の画像内での座標を取得する機会がありました。本記事では、そのための方法についての備忘録をまとめます。
OpenCV の SIFT (Scale-Invariant Feature Transform) を用いて、テンプレート画像と元の画像を特徴点マッチングし、アフィン変換を推定して座標を取得する方法を紹介します。
実装
必要なライブラリ
pip install opencv-python numpy tqdm
Pythonコード
以下のコードでは、指定した大きな画像 (image_path
) に対して、テンプレート画像 (templates_dir
内の PNG 画像) を SIFT でマッチングし、元の画像内の座標を取得します。
import cv2
import numpy as np
from glob import glob
from tqdm import tqdm
import os
# 画像読み込み
def load_image_gray(path):
img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
if img is None:
print(f"画像が見つかりません: {path}")
return img
# 特徴点抽出
def extract_features(image, detector):
return detector.detectAndCompute(image, None)
# マッチング処理
def match_features(des1, des2, matcher, ratio_test=0.7, min_matches=4):
matches = matcher.knnMatch(des1, des2, k=2)
good_matches = [m for m, n in matches if m.distance < ratio_test * n.distance]
return good_matches if len(good_matches) >= min_matches else None
# アフィン変換推定
def estimate_affine_transform(kp1, kp2, good_matches):
src_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
M_affine, _ = cv2.estimateAffinePartial2D(src_pts, dst_pts, method=cv2.RANSAC, ransacReprojThreshold=5.0)
return M_affine
# 画像上にマッチング結果を描画
def draw_matched_rectangle(image, M_affine, templ_shape):
h, w = templ_shape
rect_pts = np.float32([[0, 0], [w, 0], [w, h], [0, h]]) # 長方形の四隅
transformed_pts = cv2.transform(np.array([rect_pts]), M_affine)[0] # 変換後の座標
cv2.polylines(image, [np.int32(transformed_pts)], isClosed=True, color=(0, 0, 255), thickness=2)
return transformed_pts
# メイン処理
def main(image_path, templates_dir, output_path):
# 画像とテンプレート一覧の読み込み
img = load_image_gray(image_path)
templ_paths = glob(templates_dir)
dst_img = cv2.imread(image_path)
# SIFT特徴量検出器 & BFMatcher 設定
sift = cv2.SIFT_create()
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)
kp1, des1 = extract_features(img, sift)
# 特徴点が見つからなかった場合
if des1 is None:
print("対象画像の特徴点が見つかりませんでした。")
return
for templ_path in tqdm(templ_paths):
templ = load_image_gray(templ_path)
if templ is None:
continue
kp2, des2 = extract_features(templ, sift)
if des2 is None:
continue
good_matches = match_features(des1, des2, bf)
if good_matches is None:
print(f"特徴点のマッチングが不足: {templ_path}")
continue
# アフィン変換推定
M_affine = estimate_affine_transform(kp1, kp2, good_matches)
if M_affine is None:
print(f"アフィン変換推定に失敗: {templ_path}")
continue
# 矩形描画
best_dst = draw_matched_rectangle(dst_img, M_affine, templ.shape)
# ファイル名を矩形の近くに表示
x, y, _, _ = cv2.boundingRect(best_dst)
base_name = os.path.splitext(os.path.basename(templ_path))[0]
cv2.putText(dst_img, base_name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 結果を保存
cv2.imwrite(output_path, dst_img)
print(f"結果画像を保存しました: {output_path}")
実行
# 実行
if __name__ == "__main__":
# パラメータ設定
IMAGE_PATH = "/xxx/default.jpg"
TEMPLATES_DIR = "/xxx/*.png"
OUTPUT_PATH = "/xxx/match_result.jpg"
main(IMAGE_PATH, TEMPLATES_DIR, OUTPUT_PATH)
まとめ
本記事では、SIFT を用いた特徴点マッチング によって、部分画像が元画像のどこに位置するかを推定し、アフィン変換 で位置を特定する方法を紹介しました。
✅ 特徴点の抽出には SIFT を使用(OpenCV 4.4 以降は自由に使用可能)
✅ BFMatcher で特徴点をマッチングし、RANSAC でノイズを除去
✅ アフィン変換で座標を推定し、矩形で元画像に描画
✅ 結果画像を保存し、どの部分画像がどこにあるかを可視化
この方法を活用すれば、古地図の部分画像の位置特定、OCR の領域検出、画像比較などにも応用できます。
📌 今後の課題
-
画像が回転している場合の補正
-
SIFT より高速なアルゴリズムの検討(ORB, AKAZE など)
-
処理速度の最適化(特徴点のフィルタリング)
以上、不完全な点もあるかもしれませんが、参考になりましたら幸いです。 📝
Discussion