大きな画像から部分画像の座標を取得する方法
概要 大きな画像の一部が切り出された複数の画像から、元の画像内での座標を取得する機会がありました。本記事では、そのための方法についての備忘録をまとめます。 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 を用いた特徴点マッチング によって、部分画像が元画像のどこに位置するかを推定し、アフィン変換 で位置を特定する方法を紹介しました。 ...
