特徴量マッチングによるテンプレートマッチング

この記事では、特徴量マッチングを用いたテンプレートマッチングについて詳しく解説します。具体的なコード例も提供しますので、PythonOpenCVを使用した画像処理に興味がある方はぜひ参考にしてください。

目次

  1. 特徴量とは
  2. 特徴点検出アルゴリズム
  3. 特徴量を用いたテンプレートマッチング
  4. Python および OpenCV を使った実装例

特徴量とは

画像の「特徴」(Feature)とは、その画像が他の画像と区別できる何らかの情報です。これらの情報から抽出される「特性」や「属性」が「特征値」または「ディスクリプタ」と呼ばれます。


特定点検出アルゴリズム

SIFT(Scale-Invariant Feature Transform)、SURF(Speeded Up Robust Features)、ORB(Oriented FAST and Rotated BRIEF)などがあります。これらのアルォリズムでは、「角度」、「色」、「強度」などの視覚的要素から一意的なディスクリプタを生成します。


特定値を用いたテンプレートマッチング

ここでいう"Template Matching"では、大きな画像(ソース画像)内から小さな画像(テンプレート)がどこに存在するか探す代わりに、両者間で共有されているキーポイント(features)やそれら間の関係性等を探し求めます。これら一連流れにより複雑且つ多様化した状況下でも安定的なマッチング結果を得ることが可能です。


Python および OpenCV を使った実装例

以下にPythonOpenCVで特徴量マッチングに基づくテンプレートマッチングの実装例を示します。

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()