この記事では、特徴量マッチングを用いたテンプレートマッチングについて詳しく解説します。具体的なコード例も提供しますので、PythonとOpenCVを使用した画像処理に興味がある方はぜひ参考にしてください。
目次
特徴量とは
画像の「特徴」(Feature)とは、その画像が他の画像と区別できる何らかの情報です。これらの情報から抽出される「特性」や「属性」が「特征値」または「ディスクリプタ」と呼ばれます。
特定点検出アルゴリズム
SIFT(Scale-Invariant Feature Transform)、SURF(Speeded Up Robust Features)、ORB(Oriented FAST and Rotated BRIEF)などがあります。これらのアルォリズムでは、「角度」、「色」、「強度」などの視覚的要素から一意的なディスクリプタを生成します。
特定値を用いたテンプレートマッチング
ここでいう"Template Matching"では、大きな画像(ソース画像)内から小さな画像(テンプレート)がどこに存在するか探す代わりに、両者間で共有されているキーポイント(features)やそれら間の関係性等を探し求めます。これら一連流れにより複雑且つ多様化した状況下でも安定的なマッチング結果を得ることが可能です。
Python および OpenCV を使った実装例
以下にPythonとOpenCVで特徴量マッチングに基づくテンプレートマッチングの実装例を示します。
import cv2 import numpy as np # ソース画像・テンプレート画像読み込み src_img = cv2.imread('source.png',0) template_img = cv2.imread('template.png',0) # ORB検出器生成 & キーポイントとディスクリプタ計算 orb = cv2.ORB_create() kp1, des1 = orb.detectAndCompute(src_img,None) kp2, des2 = orb.detectAndCompute(template_img,None) # BFMatcherオブジェクト生成 & マッチング計算 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1,des2) # マッチング結果ソート(距離順) matches_sorted= sorted(matches, key=lambda x:x.distance) # マッチング結果描画(上位10件) result_img= cv2.drawMatches(src_img,kp1,template_img,kp2, matches_sorted[:10],None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) cv2.imshow("Matching result", result_img) cv2.waitKey(0) cv2.destroyAllWindows()
このコードではORBアルゴリズムを使用していますが、SIFTやSURF等他のアルゴリズムに変更する事も可能です。また、BFMatcher
の代わりにFlannBasedMatcher
等他のマッチャーも利用可能です。
その他 適応的ヒストグラム均等化+Cannyエッジ検出器によるテンプレートマッチング フーリエ変換+高周波強調フィルタリング+Cannyエッジ検出器によるテンプレートマッチング Gaborフィルター+ Cannyエッジ検出器 によるテンプレートマットイング Wavelet変換 + エージェントベースの照合法 スケール不変特徴量変換(SIFT) + エージェントベースの照合法があります。
import SimpleITK as sitk import numpy as np import cv2 # image path fixed_image_path = 'fixed_image_path' moving_image_path = 'moving_image_path' # load fixed_image = sitk.ReadImage(fixed_image_path) moving_image = sitk.ReadImage(moving_image_path) # resize fixed_resized_image = sitk.Resample(fixed_image, [800, 600], sitk.Transform(), sitk.sitkLinear, fixed_image.GetOrigin(), [800, 600], fixed_image.GetSpacing(), fixed_image.GetDirection()) # normalrize fixed_resized_image = (fixed_resized_image - np.min(fixed_resized_image)) / (np.max(fixed_resized_image) - np.min(fixed_resized_image)) moving_image = (moving_image - np.min(moving_image)) / (np.max(moving_image) - np.min(moving_image)) # 平坦化 fixed_resized_image = cv2.equalizeHist(fixed_resized_image) moving_image = cv2.equalizeHist(moving_image) # noise(Gaussian Blur) fixed_resized_image = cv2.GaussianBlur(fixed_resized_image, (5, 5), 0) moving_image = cv2.GaussianBlur(moving_image, (5, 5), 0) # image registrarion registration_method = sitk.ImageRegistrationMethod() registration_method.SetMetricAsMattesMutualInformation() registration_method.SetMetricSamplingPercentage(0.1) registration_method.SetMetricSamplingStrategy(registration_method.RANDOM) registration_method.SetInterpolator(sitk.sitkLinear) registration_method.SetOptimizerAsGradientDescent(learningRate=1.0, numberOfIterations=100, convergenceMinimumValue=1e-6, convergenceWindowSize=10) registration_method.SetOptimizerScalesFromPhysicalShift() registration_method.SetInitialTransform(sitk.TranslationTransform(fixed_resized_image.GetDimension())) final_transform = registration_method.Execute(fixed_resized_image, moving_image) # create reg img resampled_image = sitk.Resample(moving_image, fixed_resized_image, final_transform, sitk.sitkLinear, 0.0, moving_image.GetPixelID()) # top x y resampled_array = sitk.GetArrayFromImage(resampled_image) resampled_array = np.transpose(resampled_array, (1, 2, 0)) resampled_array = cv2.cvtColor(resampled_array, cv2.COLOR_GRAY2RGB) cv2.rectangle(resampled_array, (0, 0), (800, 600), (0, 255, 0), 2) # print result_image_path = 'result_image_path' cv2.imwrite(result_image_path, resampled_array) cv2.imshow('Result Image', resampled_array) cv2.waitKey(0) cv2.destroyAllWindows()