OpenCVのORB_create関数を使ったORB特徴点の抽出とマッチング

概要

この記事では特徴点マッチングの手法の一つをご紹介しています。ORB特徴点抽出とマッチングは、画像認識を行うアプリケーションでよく使用されています。ORB(Oriented FAST and Rotated BRIEF)は、画像内の特徴的な点(キーポイント)を検出し、それらの点の周囲にパターン(記述子)を生成します。これにより、異なる画像間で同じオブジェクトやパターンを識別することが可能になります。例えば、物体認識、画像検索、画像ステッチング(パノラマ作成)、3D再構成などのタスクで使用されています。下記のサンプルをもとに動作させてみましょう。

出力結果

OpenCVのORB_create関数を使ったORB特徴点の抽出とマッチング

プログラム全体

import urllib.request import cv2 import matplotlib.pyplot as plt import numpy as np # 画像をダウンロード image_urls = [ 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/box.png', 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/box_in_scene.png' ] images = [] for url in image_urls: with urllib.request.urlopen(url) as response: s = response.read() arr = np.asarray(bytearray(s), dtype=np.uint8) img = cv2.imdecode(arr, -1) # 'そのまま読み込む' img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGRをRGBに変換 images.append(img) # 両画像に対してキーポイントと記述子を計算 kp1, des1 = orb.detectAndCompute(images[0], None) kp2, des2 = orb.detectAndCompute(images[1], None) # BFMatcherオブジェクトを作成 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) # 記述子をマッチング matches = bf.match(des1, des2) # マッチングを距離でソート(小さいほど良い) matches = sorted(matches, key=lambda x: x.distance) # 最初の50のマッチングを描画 matching_result = cv2.drawMatches(images[0], kp1, images[1], kp2, matches[:50], None, flags=2) # BGRをRGBに変換 matching_result = cv2.cvtColor(matching_result, cv2.COLOR_BGR2RGB) # マッチング結果を表示 plt.figure(figsize=(15, 15)) plt.imshow(matching_result) plt.axis('off') plt.show()

解説

解説1: ライブラリのインポート

import urllib.request import cv2 import matplotlib.pyplot as plt import numpy as np

この部分では、プログラムで使用するライブラリをインポートしています。

urllib.request URLを通じてデータを取得するためのライブラリです。このプログラムでは、インターネット上から画像をダウンロードするために使用します。
cv2 OpenCVと呼ばれる画像処理ライブラリです。画像の読み込み、変換、特徴量抽出など、様々な画像処理を行うことができます。
matplotlib.pyplot データの可視化を行うためのライブラリです。このプログラムでは、画像を表示するために使用します。
numpy 数値計算を行うためのライブラリです。配列や行列の操作を効率的に行うことができます。

解説2: 画像のダウンロードと前処理

# 画像をダウンロード image_urls = [ 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/box.png', 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/box_in_scene.png' ] images = [] for url in image_urls: with urllib.request.urlopen(url) as response: s = response.read() arr = np.asarray(bytearray(s), dtype=np.uint8) img = cv2.imdecode(arr, -1) # 'そのまま読み込む' img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGRをRGBに変換 images.append(img)

この部分では、指定したURLから画像をダウンロードし、前処理を行っています。前処理としては、画像の色空間をBGR(Blue-Green-Red)からRGB(Red-Green-Blue)に変換しています。これは、OpenCVがBGRをデフォルトの色空間として扱う一方で、matplotlibはRGBをデフォルトとして扱うため、後で画像を表示する際に色が正しく表示されるようにするための処理です。 ライブラリや機能によって色空間情報に差がありますので、画像操作の際には処理する画像や扱うライブラリの色空間を意識するのも一つのポイントです。

解説3: 特徴量の抽出

# 両画像に対してキーポイントと記述子を計算 kp1, des1 = orb.detectAndCompute(images[0], None) kp2, des2 = orb.detectAndCompute(images[1], None)

この部分では、ORB (Oriented FAST and Rotated BRIEF) 特徴量抽出器を使って、2つの画像(images[0]images[1])からそれぞれの特徴点(キーポイント)とその特徴点の記述子を計算しています。特徴点とは、画像内の特徴的な位置(エッジやコーナーなど)を指し、記述子はその特徴点の周囲のパターンを表現したものです。これらは、画像間の対応関係を見つけるために使用されます。

orb.detectAndCompute(image, mask)は、指定した画像から特徴点(キーポイント)を検出し、それらの特徴点の記述子を計算する関数です。ここで、imageは特徴点を検出したい画像で、maskは特徴点を検出する領域を指定するマスク画像です。maskNoneの場合は、画像全体から特徴点を検出します。

この関数は2つの値を返します。kpは検出された特徴点(キーポイント)のリストで、desはそれらの特徴点の記述子を表すNumpy配列です。特徴点の記述子は、その特徴点の周囲の画像パターンを表現したもので、これを使って異なる画像間で同じ特徴点をマッチングすることができます。

解説4: 特徴量のマッチング

# BFMatcherオブジェクトを作成 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) # 記述子をマッチング matches = bf.match(des1, des2) # マッチングを距離でソート(小さいほど良い) matches = sorted(matches, key=lambda x: x.distance) # 最初の50のマッチングを描画 matching_result = cv2.drawMatches(images[0], kp1, images[1], kp2, matches[:50], None, flags=2)

この部分では、抽出した特徴量を元に、2つの画像間で特徴点のマッチングを行っています。マッチングとは、ある画像の特徴点が他の画像のどの特徴点と一致するかを見つけることを指します。 マッチングにはBFMatcher(Brute-Force Matcher)を使用しています。BFMatcherでは全ての特徴点の組み合わせに対して距離を計算し、最も近い特徴点をマッチとして選択しています。ここで、cv2.NORM_HAMMINGはORB記述子の比較に適した距離計算方法(ハミング距離)を指定しており、crossCheck=Trueは相互に最良のマッチングを持つペアだけを選んでいます。 次に、matches = bf.match(des1, des2)で2つの画像の記述子間でマッチングを行っています。これにより、des1の各記述子に対してdes2の中で最も良いマッチングを見つけます。 その後、matches = sorted(matches, key=lambda x: x.distance)でマッチングを距離(記述子間のハミング距離)でソートしています。距離が小さいほど記述子が似ている(良いマッチング)ということを意味します。 最後に、matching_result = cv2.drawMatches(images[0], kp1, images[1], kp2, matches[:50], None, flags=2)で距離が小さい50個のマッチングを描画しています。cv2.drawMatchesは2つの画像とそれぞれの特徴点、マッチングの情報を元にマッチング結果を描画した画像を生成しています。

補足:ハミング距離とは?

ハミング距離とは、同じ長さの二つの文字列(例えば、二つの数字の列や二つの単語)がどれだけ違うかを表す方法です。具体的には、同じ位置にある文字(または数字)が異なる場合に1とし、それらを全て足し合わせた数がハミング距離になります。

例えば、1011と1001という二つの4桁の数字があるとします。これらの数字は1番目と2番目、4番目の桁が同じで、3番目の桁が異なります。したがって、この二つの数字のハミング距離は1になります。

このハミング距離は、二つの情報がどれだけ似ているか(または違うか)を数値化する方法として、コンピュータの世界でよく使われます。例えば、画像認識のような場面で、二つの画像がどれだけ似ているかを判断するために使われることがあります。

4ビットの文字列のハミング距離を図示すると、それぞれの4ビット文字列がどれだけ他の4ビット文字列と異なるかを視覚的に理解することができます。例えば、4ビットの文字列は0000から1111まで全部で16通りあります。これらの文字列を全て書き出し、任意の2つの文字列間のハミング距離を計算し、それを図示すると下図の通りです。

この図を見ることで、例えばどの4ビット文字列が他の文字列とどれだけ異なるか、あるいは特定のハミング距離を持つ文字列がどれだけ存在するかなどを視覚的に理解することができます。

解説5:マッチング結果の表示

# BGRをRGBに変換 matching_result = cv2.cvtColor(matching_result, cv2.COLOR_BGR2RGB) # マッチング結果を表示 plt.figure(figsize=(15, 15)) plt.imshow(matching_result) plt.axis('off') plt.show()

ここでは、マッチングの結果を画像として表示しています。マッチング結果の画像は、2つの画像を並べ、マッチした特徴点間を線で結んだものです。これにより、どの特徴点がマッチしているかを視覚的に確認することができます。

おまけ

SIFT(Scale-Invariant Feature Transform)とORB(Oriented FAST and Rotated BRIEF)は、どちらも画像から特徴点を抽出するためのアルゴリズムですが、その特性や使用目的にはいくつかの違いがあります。

特徴点の抽出と記述の方法 SIFTは、画像を色々な大きさで見て、その中で特徴点を見つけます。そして、その特徴点の周りの模様を詳しく調べます。これにより、画像が回転したり、大きさが変わったりしても、同じ点を見つけることができます。一方、ORBは、画像の中で明るさの変わる急な部分を見つけ、その点の周りの模様をざっくりと調べます。これにより、高速な特徴点の検出と効率的なバイナリ記述子の生成が可能です。
計算速度とメモリ使用量 ORBはSIFTよりも計算速度が速く、メモリ使用量も少ないです。これはORBがバイナリ記述子を使用しているためで、これにより特徴記述子の比較が高速に行うことができ、メモリ使用量を抑えられます。
精度 一般的には、SIFTの方がORBよりも精度が高いとされています。しかし、リアルタイム性が求められるアプリケーションや、計算資源が限られている環境では、ORBの方が適している場合もあります。

これらの違いから、SIFTとORBはそれぞれ異なる用途や状況に適しています。使用するアプリケーションの要件により、適切なアルゴリズムを選択することが重要です。