36
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

輪郭から四角形を検出

Posted at

OpenCVで輪郭を取得することはできますが、円などハフ変換の記事は
インターネット上にたくさんありますが、四角形を検出するところまでの記事が
ネット上では少なかったので書いて見ようかと思いました。
自分のブログでもCannyでエッジを取得して判定する記事を書きましたが
二値化処理が少し複雑で、エッセンスの部分が分かりにくかったため、
さらにシンプルなものを掲載しようかと思います。

#処理の流れ
プログラムはPythonとOpenCVを使います。

  • 画像を読み込む

  • 画像のグレイスケール・二値化

  • 輪郭を取得

  • 輪郭の近似
    近似条件の近似カーブ最大距離を輪郭長の0.02とする(0.02は任意)
    近似処理にはcv2.approxPolyDP()を使用します。

  • 各オブジェクトの輪郭四角判定

 1. 角が4つある
 2. 面積値が条件以上ある
 3. 凸形状である
  isContourConvex()を使用する
 4. 各辺のなす角の最大コサインが0.3 以下。
  三角関数コサインが0.3を取っているのは、下図を見ての調整となります。
  正方形はコサイン1.0が理想値ではあります。

三角関数グラフ
  sin_cos_graph.png

#サンプルコード

findSquares.py
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()

#入力画像
shapes_image.png
上記図形から四角形を検出します。

#結果画像
finded_squares.png
輪郭を赤で囲ったところが、検出した四角形です。
しきい値は甘めになっているので、左下の台形も検出しています。

#動作環境
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

36
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?