どんどん続きを進めます。
次はハフ変換による円検出。
ハフ変換による円検出 — OpenCV-Python Tutorials 1 documentation
ハフ変換による円検出
円上の点は、以下の式を満たします。
直線検出のときと同様、この式のパラメータ()の空間に、 その円上に乗った点の数をマッピングする感じかと。
直線検出と比べてパラメータが1つ多いため、単純に全パラメータ空間を 走査するのは非効率ということで、何がしかの手法を使っているようです。
前回と同じ画像を対象とします。
シールの領域を取得できるといいなと。
import cv2 img1 = cv2.imread('harupan_200317_1.jpg') img2 = cv2.imread('harupan_210402_1.jpg') img3 = cv2.imread('harupan_210402_2.jpg') img1 = cv2.resize(img1, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_AREA) img2 = cv2.resize(img2, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_AREA) img3 = cv2.resize(img3, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_AREA) import numpy as np img123 = np.hstack((img1,img2,img3)) cv2.imshow('harupan', img123)
OpenCVでのハフ変換(円検出)関数
処理の内容はさておき、ハフ変換(円検出)の関数の使い方を確認します。
特徴検出 — opencv 2.2 documentation
【OpenCV】cv2.HoughCircles()の使い方【円を検出する】 | 資格マフィア
ハフ変換(円検出)の中でCannyエッジ検出が行われているようです。
前回は自分でグレースケール化、エッジ検出をやりましたが、今回はグレースケール化するだけになります。
引数が色々ありますが、以下のように設定します。
- dp : 投票空間(パラメータ空間?)の比率の逆数とのこと。参考サイトでは0.8~1.2がよさそうと書いてありますが、1と1.5ぐらいを試してみるかな。
- minDist : 円の中心同士の最小距離。今回は、シールが横に5個並んでいて、台紙が画像(横約300pixel)の横半分ぐらいに写っているものもあるので、シール間は約30pixel、値は20ぐらいにしておくかな。
- param1 : Cannyエッジ検出の閾値の大きいほうということで、前回使った 200 を与えます。
- param2 : 円検出の投票数閾値とのこと。シールが一番小さく写っている画像で直径30pixelぐらいなので、円周100pixelぐらい、閾値は50ぐらいにしておきます。 → やってみると、円が全く検出されなかったので、結局25にしました。
- minRadius : 最小半径、10にします。
- maxRadius : 最大半径、30にします。
img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) img3_gray = cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY) circles1 = cv2.HoughCircles(img1_gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=200, param2=25, minRadius=10, maxRadius=30) circles2 = cv2.HoughCircles(img2_gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=200, param2=25, minRadius=10, maxRadius=30) circles3 = cv2.HoughCircles(img3_gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=200, param2=25, minRadius=10, maxRadius=30)
計算結果を確認すると、
>>> circles1.shape (1, 40, 3) >>> circles2.shape (1, 33, 3) >>> circles3.shape (1, 23, 3) >>> circles1[0,0,:] array([221.5, 329.5, 27.2], dtype=float32)
まあ妥当か。
結果の次元の先頭に謎の次元が付いています。
描画します。
circles1 = np.uint16(np.around(circles1)) circles2 = np.uint16(np.around(circles2)) circles3 = np.uint16(np.around(circles3)) imgs = [img1.copy(), img2.copy(), img3.copy()] circles = [circles1, circles2, circles3] for i in range(3): for c in circles[i][0]: imgs[i] = cv2.circle(imgs[i], (c[0],c[1]),c[2],(0,255,0),2) imgs123 = np.hstack((imgs[0], imgs[1], imgs[2])) cv2.imshow('Images', imgs123)
まあまあといったところでしょうか。
- 一番左の画像では全シールが検出されています。形がきちんとした丸ではないにも関わらず。
ただし中心、半径は少しずれていたりします。 - 他の画像では、検出できていないシールがあります。
- 全画像で、台紙下側で円を誤検出しています。細かい模様があって、エッジ検出したときにパターンが発生してしまうからか。
パラメータを変えてやってみます。
- dp : 少し増やして1.5
- param2 : パラメータ空間が小さくなるということは、1つのパラメータ組み合わせに対する投票数が増えるのでは、ということで50
circles1 = cv2.HoughCircles(img1_gray, cv2.HOUGH_GRADIENT, dp=1.5, minDist=20, param1=200, param2=50, minRadius=10, maxRadius=30) circles2 = cv2.HoughCircles(img2_gray, cv2.HOUGH_GRADIENT, dp=1.5, minDist=20, param1=200, param2=50, minRadius=10, maxRadius=30) circles3 = cv2.HoughCircles(img3_gray, cv2.HOUGH_GRADIENT, dp=1.5, minDist=20, param1=200, param2=50, minRadius=10, maxRadius=30) circles1 = np.uint16(np.around(circles1)) circles2 = np.uint16(np.around(circles2)) circles3 = np.uint16(np.around(circles3)) imgs = [img1.copy(), img2.copy(), img3.copy()] circles = [circles1, circles2, circles3] for i in range(3): for c in circles[i][0]: imgs[i] = cv2.circle(imgs[i], (c[0],c[1]),c[2],(0,255,0),2) imgs123 = np.hstack((imgs[0], imgs[1], imgs[2])) cv2.imshow('Images', imgs123)
だいたい同じような、ただ一番右の画像の結果は悪くなっています。
元の画像シール領域のサイズが小さいので、粗いパラメータ刻みではうまくいかなかった、ということかと。
以上
シール点数集計には使いにくそうなので、このあたりにしておきます。
まだまだチュートリアルは続きます。