円形度とは、図形の面積と周囲長の関係から、円らしさの値を求めます。
円形度の詳細は以下のページを参照ください。
OpenCV的には、図形の面積はcontourArea()関数で、周囲長はarcLength()関数で求める事ができるので、これを使って円形度を求めます。
今回は、下図のように手書きで書いた図形の中から円らしい部分を見つけたいと思います。
サンプルプログラム
contourArea()関数と、周囲長はarcLength()関数を使って円らしい部分を抽出するサンプルプログラムを作成しました。
円形度を求める部分は関数(circularity)にしてあります。
import cv2
import numpy as np
def circularity(contour):
'''
円形度を求める
Parameters
----------
contour : ndarray
輪郭の(x,y)座標の配列
Returns
-------
円形度
'''
# 面積
area = cv2.contourArea(contour)
# 周囲長
length = cv2.arcLength(contour, True)
# 円形度を返す
return 4*np.pi*area/length/length
# 8ビット1チャンネルのグレースケールとして画像を読み込む
img = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
# 白黒反転して二値化
ret, img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY_INV)
# 一番外側の輪郭のみを取得
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE )
# 画像表示用に入力画像をカラーデータに変換する
img_disp = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# 全ての輪郭を描画
cv2.drawContours(img_disp, contours, -1, (0, 0, 255), 2)
# 輪郭の点の描画
for contour in contours:
# 円形度の計算
val = circularity(contour)
# 輪郭の矩形領域
x,y,w,h = cv2.boundingRect(contour)
# 円形度の描画
cv2.putText(img_disp, f"{val:.3f}", (x, y-10), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 1, cv2.LINE_AA)
# 円らしい領域(円形度が0.85以上)を囲う
if val > 0.85:
cv2.rectangle(img_disp,(x-5,y-5),(x+w+5,y+h+5),(255,0,0),2) # 少し外側を囲う
cv2.imshow("Image", img_disp)
# キー入力待ち(ここで画像が表示される)
cv2.waitKey()
実行結果
実行結果を見ると、だいたい円っぽい部分は円形度を使うと抽出することが出来ますが、真円度的な円の正確さを求めるのには、円形度は不向きな感じがします。