はじめに
- OpenCVには、画像のコーナー点を検出するcornerHarrisやgoodFeaturesToTrackなどの関数があります。
- しかし、これらの関数には癖があるようで、長方形の形(左)だとコーナーをきれいに見つけましたが菱形の形(右)だと苦手でした。赤点が検出点です。
- 感度を良くすればコーナーを見つけますが、エッジ上の点を誤検出するケースが増えます。感度を下げれば誤検出は減りますが、コーナーを見落としやすくなります。
- そこで、感度良く検出したあとから、誤検出したエッジ点を除く方法を考えて、試しました。
工夫したこと
-
goodFeaturesToTrackの結果からスタート
画像の全各要素をフィルターすると時間がかかるので、途中から始めて処理量削減。 -
白図形の凸コーナーのみの検出に限定
限定することで、フィルター精度向上。 -
フィルターは対照点の周囲の明るさをチェックして判断
Fastアルゴリズムのアイデアを借用しました。
内容
1. PC環境
実施したPC環境は以下のとおりです。
CPU | Celeron N4100 |
メモリ | 8GB LPDDR4 |
2. 前準備
Windowsで使えるようにするため、以下のツール / システムをインストールしました。
インストール時に参考にしたサイトも記載します。
-
python(使用したのはVer.3.7 )
実行時のベースシステムです。
(参考)https://qiita.com/ssbb/items/b55ca899e0d5ce6ce963 -
pip(使用したのはVer.21.2.4 )
他のツールをダウンロードする際に使うツールです。
(python3系ではバージョン3.4以降であれば、pythonのインストールと共にpipもインストールされます。)
(参考)https://gammasoft.jp/python/python-library-install/ -
OpenCV(使用したのはVer.4.5.3.56 )
画像系処理するためのライブラリです。
(参考)https://qiita.com/ideagear/items/3f0807b7bde05aa18240 -
numpy(使用したのVer.4.7.0.72)
行列計算が得意な数値計算モジュールです。
(参考)https://qiita.com/butako/items/15d7cb5aaef90b09ccd8
3. pyファイルの作成
組んだコードは次のとおりです。
import numpy as np
import cv2 as cv
camera = cv.VideoCapture(0) # カメラCh.(ここでは0)を指定
#周囲のリスト(中心点からの相対)#
l_2d_a = [ \
[ 0, 3], \
[ 1, 3], \
[ 2, 2], \
[ 3, 1], \
[ 3, 0], \
[ 3,-1], \
[ 2,-2], \
[ 1,-3], \
[ 0,-3], \
[-1,-3], \
[-2,-2], \
[-3,-1], \
[-3, 0], \
[-3, 1], \
[-2, 2], \
[-1, 3], \
]
ret, img = camera.read() # フレームを取得
h, w, _ = img.shape #フレームの高さと幅を取得
#フィルタリングのためのリスト変数
up_lst_a = list() #周囲で黒→白に変わった場所のリスト変数
dwn_lst_a = list() #周囲で白→黒に変わった場所のリスト変数
l_a = list() #周囲の明るさのリスト変数
#フィルタリングのパラメータ
th3 = 15 #白と判断する下限値(中心点の明るさからの相対)
th5 = 0.40 #コーナーと判断する白エリアの最大割合
th6 = 0.6 #コーナーと判断する中心点の明るさ割合(周囲の明るさに対する相対)
try:
while True:
ret, img = camera.read() #フレームを取得
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY) #モノクロ化
gray = np.float32(gray) #float32に型変換
#コーナー候補検出#
corners= cv.goodFeaturesToTrack(gray, 300, 0.01, 1)
#各コーナー候補についてチェック#
for corner in corners:
x,y= corner[0] #中心点座標を入手
x= int(x)
y= int(y)
#フィルタリング#
if x > 2 and x < w-3 and y > 2 and y < h-3 :
#初期化#
up_lst_a.clear()
dwn_lst_a.clear()
l_a.clear()
cnt = 0
if gray[y,x] > gray[y + l_2d_a[-1][0], x + l_2d_a[-1][1]] + th3:
sg= -1
else:
sg = 1
#周囲をチェック#
for pt in l_2d_a:
if gray[y,x] > gray[y + pt[0], x + pt[1]] + th3:
if sg > 0:
dwn_lst_a.append(cnt) #白→黒に変わってたらリストに追加
sg = -1
else:
if sg < 0:
up_lst_a.append(cnt) #黒→白に変わってたらリストに追加
sg = 1
l_a.append(gray[y + pt[0], x + pt[1]]) #周囲の明るさをリストに追加
cnt = cnt + 1
if len(up_lst_a) == 1 and len(dwn_lst_a) == 1 : #黒→白、白→黒が1回だけ。つまり白エリアが1つ
#白エリアの割合を計算#
siro_a = (dwn_lst_a[0]-up_lst_a[0])/len(l_2d_a)
if siro_a<0:
siro_a = siro_a + 1
if siro_a<th5: #白エリアの割合がth5より小さい
if (gray[y,x]-min(l_a))/(max(l_a)-min(l_a))>th6: #中心点の明るさが周囲の明るさの幅に対し、下限からth6より明るい
cv.rectangle(img, (x-1,y-1),(x+1,y+1),(0,255,0),-1) #フィルタリングをクリアした点を■マーク
cv.imshow('frame',img) #チェック結果を表示
if cv.waitKey(1) & 0xFF == ord('q'): #’q’キーを押したら、LOOPから抜ける
break
cv.destroyWindow('frame')
camera.release()
except KeyboardInterrupt:
cv.destroyWindow('frame')
camera.release()
4. 説明
1. フィルターで使用する周囲の点
対象点の周囲半径3ピクセルのあたりの点、16点を使いました。
2. フィルターの3つの条件
1)明るい範囲(白エリア)と暗い範囲(黒エリア)が1つずつ
周囲の点を順に並べたときに白→黒と黒→白の変化点の数がそれぞれ1つなこと。
白と黒の境目は対照点の明るさよりth3
だけ暗いところとしました。
境目を越えたら、その点をlistに追加して、数をカウントして調べてます。
#周囲をチェック#
for pt in l_2d_a:
if gray[y,x] > gray[y + pt[0], x + pt[1]] + th3:
if sg > 0:
dwn_lst_a.append(cnt) #白→黒に変わってたらリストに追加
sg = -1
else:
if sg < 0:
up_lst_a.append(cnt) #黒→白に変わってたらリストに追加
sg = 1
cnt = cnt + 1
if len(up_lst_a) == 1 and len(dwn_lst_a) == 1 : #黒→白、白→黒が1回だけ。つまり白エリアが1つ
2)白エリアの方が小さい
白の凸角のみ探すため、この条件をつけました。
周囲の点を順に並べたときに白→黒と黒→白の変化点の位置で白エリアの幅を計算してます。
周囲の点数(=16)に対する白エリアの幅の割合がth5
より小さいならOKとしてます。
#白エリアの割合を計算#
siro_a = (dwn_lst_a[0]-up_lst_a[0])/len(l_2d_a)
if siro_a<0:
siro_a = siro_a + 1
if siro_a<th5: #白エリアの割合がth5より小さい
3)対象点の明るさは白エリアに近い
調べてみると、エッジ点は暗くなりがちなことがわかりました。
周囲の点の明るさの最大値-最小値の間のどのあたりに対象点があるかで判断してます。
明るさの最大値-最小値の幅に対して対象点の明るさ-最小値の割合がth6
より大きいならOKとしてます。
if (gray[y,x]-min(l_a))/(max(l_a)-min(l_a))>th6: #中心点の明るさが周囲の明るさの幅に対し、下限からth6より明るい
5. 結果
環境によって適切値は変わると思いますが、パラメータを
th3=15、th5=0.40、th6=0.6
に調整しました。
100%ではないです(除けてないエッジ点、除かれたコーナー点がでます)が、向きによらず、かなり精度よくコーナー点だけ抜き出せるようになりました。緑点が検出点です。
参考
参考記事
goodFeaturesToTrack)
https://qiita.com/hoshianaaa/items/d22fcb67d940e08b242b
参考書籍
画像認識 (機械学習プロフェッショナルシリーズ) 原田達也 (著) 講談社