OpenCVで輪郭を取得することはできますが、円などハフ変換の記事は
インターネット上にたくさんありますが、四角形を検出するところまでの記事が
ネット上では少なかったので書いて見ようかと思いました。
自分のブログでもCannyでエッジを取得して判定する記事を書きましたが
二値化処理が少し複雑で、エッセンスの部分が分かりにくかったため、
さらにシンプルなものを掲載しようかと思います。
#処理の流れ
プログラムはPythonとOpenCVを使います。
-
画像を読み込む
-
画像のグレイスケール・二値化
-
輪郭を取得
-
輪郭の近似
近似条件の近似カーブ最大距離を輪郭長の0.02とする(0.02は任意)
近似処理にはcv2.approxPolyDP()を使用します。 -
各オブジェクトの輪郭四角判定
1. 角が4つある
2. 面積値が条件以上ある
3. 凸形状である
isContourConvex()を使用する
4. 各辺のなす角の最大コサインが0.3 以下。
三角関数コサインが0.3を取っているのは、下図を見ての調整となります。
正方形はコサイン1.0が理想値ではあります。
#サンプルコード
import cv2
import math
import numpy as np
# pt0-> pt1およびpt0-> pt2からの
# ベクトル間の角度の余弦(コサイン)を算出
def angle(pt1, pt2, pt0) -> float:
dx1 = float(pt1[0,0] - pt0[0,0])
dy1 = float(pt1[0,1] - pt0[0,1])
dx2 = float(pt2[0,0] - pt0[0,0])
dy2 = float(pt2[0,1] - pt0[0,1])
v = math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) )
return (dx1*dx2 + dy1*dy2)/ v
# 画像上の四角形を検出
def findSquares(bin_image, image, cond_area = 1000):
# 輪郭取得
contours, _ = cv2.findContours(bin_image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for i, cnt in enumerate(contours):
# 輪郭の周囲に比例する精度で輪郭を近似する
arclen = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, arclen*0.02, True)
#四角形の輪郭は、近似後に4つの頂点があります。
#比較的広い領域が凸状になります。
# 凸性の確認
area = abs(cv2.contourArea(approx))
if approx.shape[0] == 4 and area > cond_area and cv2.isContourConvex(approx) :
maxCosine = 0
for j in range(2, 5):
# 辺間の角度の最大コサインを算出
cosine = abs(angle(approx[j%4], approx[j-2], approx[j-1]))
maxCosine = max(maxCosine, cosine)
# すべての角度の余弦定理が小さい場合
#(すべての角度は約90度です)次に、quandrangeを書き込みます
# 結果のシーケンスへの頂点
if maxCosine < 0.3 :
# 四角判定!!
rcnt = approx.reshape(-1,2)
cv2.polylines(image, [rcnt], True, (0,0,255), thickness=2, lineType=cv2.LINE_8)
return image
def main():
image = cv2.imread('shapes_image.png', cv2.IMREAD_COLOR)
if image is None :
exit(1)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, bw = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
rimage = findSquares(bw, image)
cv2.imshow('Square Detector', rimage)
c = cv2.waitKey()
return 0;
if __name__ == '__main__':
main()
#結果画像
輪郭を赤で囲ったところが、検出した四角形です。
しきい値は甘めになっているので、左下の台形も検出しています。
#動作環境
Windows10
Anaconda 3
Python 3.9.0
OpenCV 4.4.0
numpy 1.19.2
#まとめ
このやり方はdocs.opencv.org - samples/cpp/squares.cppのサンプルコードを見て、
それを参考にしています。
幾何学計算だけなので、このトリックはそれほど難しくなく、うまいやり方だなと
感心しました。
実際に画像処理として使うには、二値化又はエッジ検出と輪郭近似の調整が
肝となるかと思っています。
#参考
Emotion Explorer - OpenCV 四角形の検出
docs.opencv.org - samples/cpp/squares.cpp
docs.opencv.org - Structural Analysis and Shape Descriptors