OpenCVを使ったPythonでの画像処理について、ここではコーナー検出(Corner Detection)について扱っていこうと思います。
コーナー検出にも様々ありますが、ここではHarrisコーナー検出と、Shi-Tomasiコーナー検出を扱います。
この検出法の数学的な意味を理解するには専門的な知識が必要になりますので、ここでの説明の範囲を超えてしまうので具体的に扱うことはしません。意味合いとしては、画像の領域を移動させて特徴の変化量をみて、コーナーでの変化量は大きくなるということを検出します。
数式的なところに関心のある方は次のページなどを参考に理解してみてください。
- Harrisコーナー検出
- Shi-Tomasiコーナー検出
それではコーナー検出についてみていきましょう。
コーナー検出で使う画像の読み込み
まず、ライブラリのインポートとここで扱う画像の読み込みを行っていきます。
ここでもjupyter notebookを使っていきます。
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
いつものようにOpenCV、NumPy、Matplotlibをインポートしています。
ここでは、imagesフォルダの中に、chess.pngとchess2.jpgを用意しています。どちらもチェス盤の画像です。
これらを読み込んでいきましょう。
chess_board = cv2.imread('images/chess.png')
chess_board = cv2.cvtColor(chess_board,cv2.COLOR_BGR2RGB)
plt.imshow(chess_board)
imread()で画像を読み込み、cvtColor()でBGRカラーからRGBカラーに変換して、imshow()で画像を表示します。
表示すると、こうなります。
コーナー検出ではグレースケールの画像で特徴を取り出す処理を行いますので、この画像をグレースケールで表示してみます。
gray_chess_board = cv2.cvtColor(chess_board,cv2.COLOR_BGR2GRAY)
plt.imshow(gray_chess_board,cmap='gray')
cvtColor()でグレーに変換し、cmapに「gray」を指定して画像を表示します。
もう1つ、もう少し複雑なチェス盤の動画を読み込んでみます。
real_chess_board = cv2.imread('images/chess2.jpg')
real_chess_board = cv2.cvtColor(real_chess_board,cv2.COLOR_BGR2RGB)
plt.imshow(real_chess_board)
やっていることは、上と同様です。
表示してみます。
こちらもまた、グレースケールで読み込んでみます。
gray_real_chess_board = cv2.cvtColor(real_chess_board,cv2.COLOR_BGR2GRAY)
plt.imshow(gray_real_chess_board,cmap='gray')
表示してみます。
これらの画像を元に、コーナー検出を行っていきましょう。
Harrisコーナー検出
まず、Harrisコーナー検出を見ていきます。
これは画素の位置をあらゆる方向に移動させて、画素数がどのように変化するか見ることでコーナーを検出します。
上で読み込んだ画像を使ってHarrisコーナー検出を行ってみましょう。まずはgray_chess_boardの画像を使ってみます。
gray = np.float32(gray_chess_board)
dst = cv2.cornerHarris(src=gray,blockSize=2,ksize=3,k=0.04)
dst = cv2.dilate(dst,None)
chess_board[dst>0.01*dst.max()]=[255,0,0]
plt.imshow(chess_board)
まず、画像をnp.flpat32()を使って、単精度浮動小数点型にキャストします。
cornerHarris()でコーナー検出を行います。srcに画像データを渡します。blocksizeはコーナー検出の際に考慮する隣接する領域のサイズでここでは2を渡してみました。ksizeはsobel()の勾配パラメーターのカーネルサイズです。最後のkはハリス検出でのパラメーターでコーナーを判定する閾値として与えます。
sobel()についてはこちらでも触れています。
dilate()を使って膨張処理を施して特徴づけます。これはマーキングのために行っているだけですので、特にコーナー検出と直接関係のあることではありません。
dilate()についてはこちらでも扱いました。
元の画像であるchess_boardに、先ほど処理したdstが0.01*dst.max()より大きい値の部分を赤色で表示するように値を代入します。この値は処理する画像によって異なるので適切な値を調整する必要があります。
最後にimshow()で画像を表示しています。
実際に表示してみるとこうなります。
ちょっと見えにくいかもしれませんが、チェス盤のマス目の角に赤い印が表示されているのがわかります。こうして、画像内のコーナーが検出されているのがわかります。
もうひとつのリアルなチェス盤の画像でもHarrisコーナー検出をやってみましょう。
gray = np.float32(gray_real_chess_board)
dst = cv2.cornerHarris(src=gray,blockSize=2,ksize=3,k=0.04)
dst = cv2.dilate(dst,None)
real_chess_board[dst>0.01*dst.max()]=[255,0,0]
plt.imshow(real_chess_board)
読み込む画像が違うだけで、全く同じ処理をしています。
表示するとこうなります。
こちらはチェス盤のマス目の角だけでなく、チェスの駒の特徴的な部分も検出しています。
Shi-Tomasiコーナー検出
今度はShi-Tomasiコーナー検出を見ていきます。
こちらの処理は、画像中のN個のスコアが高いコーナーを検出します。
検出したいコーナーの数を指定し、検出するコーナーの質を基準に検出される2つのコーナー間の距離を与えます。これらの全情報から画像中のコーナー検出を行い、最終的にスコアが高いコーナーを抽出します。
上と同様に画像を処理していきましょう。
chess_board = cv2.imread('images/chess.png')
chess_board = cv2.cvtColor(chess_board,cv2.COLOR_BGR2RGB)
gray_chess_board = cv2.cvtColor(chess_board,cv2.COLOR_BGR2GRAY)
画像をあらためて読み込み、カラー変換して、グレースケールにしています。
corners = cv2.goodFeaturesToTrack(gray_chess_board,5,0.01,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv2.circle(chess_board,(x,y),3,255,-1)
plt.imshow(chess_board)
goodFeaturesToTrack()でコーナを検出していきます。
グレースケールの画像を与え、検出したいコーナーの数を指定します。ここでは5を指定しています。これを−1にすると全てのコーナーを検出することになります。検出するコーナーの最低限の質を0から1の間の値で指定します。ここでは0.01を与えています。検出される2つのコーナー間の最低限のユークリッド距離を与えますが、ここでは10を指定しました。指定したこれらの情報を使って画像中のコーナー検出を行います。
cornersはをnp.int0で整数にキャストしています。int0はNumpyのデータ型にはありませんが、64bitで整数化するのにこういう使い方をするようです。
for-in文を使って、cornersからコーナーデータを一つずつ取り出します。これをravel()で1次元化してx、yの座標に代入しています。このx,yを用いて、circle()を使ってコーナー部分に丸印をつけています。画像を与え、座標を指定し、半径を3、色を赤、太さを−1にして塗りつぶしの指定をしています。
これをimshow()で表示します。
実際に表示してみます。
ちょっと見えにくいですが、goodFeaturesToTrack()でコーナーを5と指定したので、5つのコーナーが赤く示されています。
これをチェス盤のマス目のコーナーをもっと検出するために、コーナーの数を64に指定して処理してみます。
corners = cv2.goodFeaturesToTrack(gray_chess_board,64,0.01,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv2.circle(chess_board,(x,y),3,255,-1)
plt.imshow(chess_board)
コーナーの指定数以外は同じ処理です。
表示するとこうなります。
コーナーの検出数が増えているのがわかります。
今度は、もう一つの画像の方で同じ処理をしてみます。
real_chess_board = cv2.imread('images/chess2.jpg')
real_chess_board = cv2.cvtColor(real_chess_board,cv2.COLOR_BGR2RGB)
gray_real_chess_board = cv2.cvtColor(real_chess_board,cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray_real_chess_board,80,0.01,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv2.circle(real_chess_board,(x,y),3,255,-1)
plt.imshow(real_chess_board)
読み込む画像を変更して、指定するコーナーの数を80にした以外は上と同じ処理になっています。
画像を表示してみましょう。
チェス盤のマス目の角以外にもチェスの駒もいくつか検出されているのがわかります。コーナーとしての品質の低いものは検出されていません。このあたりはデータをソート処理して品質の高いものから表示し、品質に満たないものは拒否されていることを示しています。
最後に
ここではOpenCVを使ったPythonでの画像処理について、ここではコーナー検出(Corner Detection)について扱ってきました。
ここではHarrisコーナー検出と、Shi-Tomasiコーナー検出の2つの検出方法を見てきました。数学的な意味には触れていません。興味のある方は専門的な情報に触れましょう。
それぞれのコーナー検出に特徴がありますが、画素を移動させてその画素数の違いから特徴を見出してコーナーを検出しているという処理をしています。