KURORO BLOGのロゴ

このエントリーをはてなブックマークに追加
OpenCVで使われるfindContoursとは?利用する注意点などを解説

OpenCVで使われるfindContoursとは?利用する注意点などを解説

今回はOpenCVで使われるfindContoursに関して、利用する注意点から各引数の使い方まで解説いたしました。なんとなくfindContoursを使っていた方へおすすめです。是非最後までご覧ください。

目次
  1. そもそもOpenCVで使われるfindContoursとは?
  2. OpenCVで使われるfindContours関数の定義
    1. OpenCVのバージョン確認方法
  3. findContours関数を用いて輪郭を検出する前に
    1. 二値画像へ加工する方法
  4. findContours関数の第二引数(mode)で指定される、タイプについて調べてみた
  5. findContours関数の第三引数(method)で指定される、タイプについて調べてみた
  6. まとめ
  7. 参考文献
目次を開く⬇︎

そもそもOpenCVで使われるfindContoursとは?

OpenCVで使われるfindContoursとは、輪郭を検出して、画像内に含まれるオブジェクト(物体)を取得する関数を意味します。

輪郭を具体的な画像を用いて説明すると、

上記画像の灰色の画素(点)=輪郭になります。

輪郭 : 同じ色や値を持つ画素(点)をつなげて、形成される曲線。

OpenCVで使われるfindContours関数の定義

findContours関数は、

1# cv2(OpenCV)を利用する宣言を行う。
2import cv2
3
4# findContours : 輪郭の検出を行う関数
5# 第一引数(thresh) : 二値画像情報
6# 二値画像とは? : https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%80%A4%E7%94%BB%E5%83%8F#:~:text=%E4%BA%8C%E5%80%A4%E7%94%BB%E5%83%8F%EF%BC%88%E3%81%AB%E3%81%A1,%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%82 
7# 第二引数(mode) : 輪郭を検出するタイプを指定する。
8# 第三引数(method) : 輪郭を形成する、画素(点)を近似する方法のタイプを指定する。
9# 戻り値  ####################### 
10# image : 輪郭付き画像情報
11# contours : 輪郭を形成する画素(点)情報
12# hierarchy : オブジェクト(物体)の階層構造情報
13# 階層構造とは? : https://wa3.i-3-i.info/word15200.html
14###############################
15
16# OpenCVのバージョンが4.0より小さい場合
17image, contours, hierarchy = cv2.findContours(thresh, mode, method)
18
19# OpenCVのバージョンが4.0以上の場合 
20contours, hierarchy = cv2.findContours(thresh, mode, method)

で定義されます。

OpenCVのバージョンにより、findContours関数の戻り値の数に違いがあります。

findContours関数を利用する際は、OpenCVのバージョンを確かめてから利用しましょう。

OpenCVのバージョン確認方法

以下の手順を踏まえて、OpenCVのバージョンを確認し、

  1. ターミナルを起動する。
  2. python -iを実行(Pythonをインタラクティブモードで実行する。ターミナルから対話型でPythonを実行できる。)
  3. import cv2を実行(cv2(OpenCV)を読み込む。)
  4. print(cv2.__version__)を実行する。(cv2(OpenCV)のバージョンをprint文を用いて確認する。)
  5. quit()(インタラクティブモードを終了する。)

findContours関数の戻り値の数を合わせるように、変更ください。

findContours関数を用いて輪郭を検出する前に

もしかすると、「findContours関数を利用すれば、どんな画像でも輪郭を検出できる!」と考える方もいるかもしれません。

しかし前処理をして適切な画像を利用しないと、「画像のどの部分を輪郭として検出すべきか」判定できません。

「画像のどの部分を輪郭として検出すべきか」判定できるように、二値画像へ加工する前処理を行う必要があります。

二値画像 : 2色のみで形成される画像。画像の輪郭の検出の際に、前処理として作成される。

二値画像へ加工する方法

二値画像へ加工するためには、

  1. cv2.cvtColor()を利用して、BGR(Blue, Green, Red)画像をグレースケール画像に変換する
  2. cv2.threshold()を利用して、二値画像を生成する

を行います。

例えば以下のようなコードを作成して、Python環境で実行すると、

1# cv2(OpenCV)を利用する宣言を行う。
2import cv2
3
4class Paint:
5    # 画像情報
6    img = None
7    # しきい値情報
8    # しきい値とは? : https://wa3.i-3-i.info/word15002.html
9    ret = None
10
11    # コンストラクタ
12    # コンストラクタとは? : https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF#:~:text=%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%EF%BC%88%E8%8B%B1%3A%20constructor%EF%BC%89%E3%81%AF,%E5%AF%BE%E7%BE%A9%E8%AA%9E%E3%81%AF%E3%83%87%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%80%82
13    # 画像の初期化
14    def __init__(self, image=None):
15        self.img = image
16
17    # 画像をグレースケールに設定する関数
18    # グレースケールとは? : https://www.shinkohsha.co.jp/blog/monochrome-shirokuro-grayscale/
19    # 1. cv2.cvtColor()を利用して、BGR(Blue, Green, Red)画像をグレースケール画像に変換する
20    def setGray(self):
21        # cvtColor : 画像の色空間(色)の変更を行う関数。
22        # cvtColorについて : https://kuroro.blog/python/7IFCPLA4DzV8nUTchKsb/
23        # 第一引数 : 多次元配列(numpy.ndarray)
24        # 第二引数 : 変更前の画像の色空間(色)と、変更後の画像の色空間(色)を示す定数を設定。
25        # cv2.COLOR_BGR2GRAY : BGR(Blue, Green, Red)形式の色空間(色)を持つ画像をグレースケール画像へ変更する。
26        self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
27
28    # グレースケール画像を二値画像へ変更する関数
29    # 2. cv2.threshold()を利用して、二値画像を生成する
30    def setBinaryImage(self):
31        # threshold : しきい値を用いて画像を二値画像へ変更する関数。
32        # thresholdについて : https://kuroro.blog/python/jofbNumJ9HtfTxnM8QHJ/
33
34        # 第一引数 : 多次元配列(numpy.ndarray)
35        # 第二引数 : しきい値。float型。
36
37        # 第三引数 : しきい値を超えた画素に対して、255を設定する。しきい値を超えていないものに関しては、0を与える。
38
39        # 第四引数 : 二値画像を判定する条件のタイプを指定する。
40        # cv2.THRESH_BINARY : (画素 <= 第二引数)の場合、画素に対して、0の値を与える。(画素 > 第二引数)の場合、画素に対して、第三引数の値を与える。
41
42        # 戻り値 #################
43        # self.ret : しきい値を返す。
44        # self.img : 多次元配列(numpy.ndarray)を返す。
45        #########################
46        self.ret, self.img = cv2.threshold(self.img, 160, 255, cv2.THRESH_BINARY)
47
48    # 画像を保存する関数
49    def writeImg(self, filePath):
50        # imwrite : 画像の保存を行う関数
51        # imwriteについて : https://kuroro.blog/python/i0tNE1Mp8aEz8Z7n6Ggg/
52        # 第一引数 : 保存先の画像ファイル名
53        # 第二引数 : 多次元配列(numpy.ndarray)
54        cv2.imwrite(filePath, self.img)
55
56# imread : 画像ファイルを読み込んで、多次元配列(numpy.ndarray)にする。
57# imreadについて : https://kuroro.blog/python/wqh9VIEmRXS4ZAA7C4wd/
58# 第一引数 : 画像のファイルパス
59ins = Paint(cv2.imread('./xxx.xxx'))
60ins.setGray()
61ins.setBinaryImage()
62ins.writeImg('./xxx.xxx')

以下の画像のように二値画像を生成できます。

imreadに関しては、OpenCVで使われるimreadとは?使い方から配列が画像になる仕組みを解説でまとめていますので、詳しく知りたい方は是非ご確認ください。

imwriteに関しては、OpenCVで使われるimwriteとは?imwriteの定義から使用例をご紹介でまとめていますので、詳しく知りたい方は是非ご確認ください。

thresholdに関しては、OpenCVで使われるthresholdとは?threshold活用例を徹底解説でまとめていますので、詳しく知りたい方は是非ご確認ください。

cvtColorに関しては、OpenCVで使われるcvtcolorとは?cvtcolorの活用例を徹底紹介でまとめていますので、詳しく知りたい方は是非ご確認ください。

findContours関数の第二引数(mode)で指定される、タイプについて調べてみた

findContours関数の第二引数(mode)では、輪郭を検出するタイプを指定します。

輪郭を検出するタイプとしては、

タイプ名称特徴
cv2.RETR_EXTERNAL                     最も外側の輪郭のみを抽出します。
cv2.RETR_LISTすべての輪郭を抽出します。一切の階層構造(オブジェクト(物体)の中にオブジェクト(物体)があったとしても、それぞれ独立したオブジェクト(物体)として捉えて、輪郭を形成する)を保持せず、輪郭を形成します。
cv2.RETR_CCOMPすべての輪郭を抽出します。二値(黒と白色)画像の黒色オブジェクト(物体)箇所の輪郭を先に形成して、次に白色オブジェクト(物体)箇所の輪郭を形成します。
cv2.RETR_TREEすべての輪郭を抽出します。階層構造(オブジェクト(物体)の中にオブジェクト(物体)が存在する)になった二値(黒と白色)画像の、外から輪郭を形成します。

があります。

例えば以下のようなコードを作成すると、

1# cv2(OpenCV)を利用する宣言を行う。
2import cv2
3
4class Paint:
5    # 画像情報
6    img = None
7    # しきい値情報
8    # しきい値とは? : https://wa3.i-3-i.info/word15002.html
9    ret = None
10    # 輪郭を形成する画素(点)情報
11    contours = None
12    # オブジェクト(物体)の階層構造情報
13    hierarchy = None
14
15    # コンストラクタ
16    # コンストラクタとは? : https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF#:~:text=%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%EF%BC%88%E8%8B%B1%3A%20constructor%EF%BC%89%E3%81%AF,%E5%AF%BE%E7%BE%A9%E8%AA%9E%E3%81%AF%E3%83%87%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%80%82
17    # 画像の初期化
18    def __init__(self, image=None):
19        self.img = image
20
21    # 画像をグレースケールに設定する関数
22    # グレースケールとは? : https://www.shinkohsha.co.jp/blog/monochrome-shirokuro-grayscale/
23    def setGray(self):
24        # cvtColor : 画像の色空間(色)の変更を行う関数。
25        # cvtColorについて : https://kuroro.blog/python/7IFCPLA4DzV8nUTchKsb/
26        # 第一引数 : 多次元配列(numpy.ndarray)
27        # 第二引数 : 変更前の画像の色空間(色)と、変更後の画像の色空間(色)を示す定数を設定。
28        # cv2.COLOR_BGR2GRAY : BGR(Blue, Green, Red)形式の色空間(色)を持つ画像をグレースケール画像へ変更する。
29        self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
30
31    # グレースケール画像を二値画像へ変更する関数
32    def setBinaryImage(self):
33        # threshold : しきい値を用いて画像を二値画像へ変更する関数。
34        # thresholdについて : https://kuroro.blog/python/jofbNumJ9HtfTxnM8QHJ/
35
36        # 第一引数 : 多次元配列(numpy.ndarray)
37        # 第二引数 : しきい値。float型。
38
39        # 第三引数 : しきい値を超えた画素に対して、255を設定する。しきい値を超えていないものに関しては、0を与える。
40
41        # 第四引数 : 二値画像を判定する条件のタイプを指定する。
42        # cv2.THRESH_BINARY : (画素 <= 第二引数)の場合、画素に対して、0の値を与える。(画素 > 第二引数)の場合、画素に対して、第三引数の値を与える。
43
44        # 戻り値 #################
45        # self.ret : しきい値を返す。
46        # self.img : 多次元配列(numpy.ndarray)を返す。
47        #########################
48        self.ret, self.img = cv2.threshold(self.img, 160, 255, cv2.THRESH_BINARY)
49
50    # 輪郭の検出を行う関数
51    def exeFindContours(self, type):
52        # findContours : 輪郭の検出を行う関数
53        # 第一引数(thresh) : 二値画像情報
54        # 第二引数(mode) : 輪郭を検出するタイプを指定する。
55        # 第三引数(method) : 輪郭を形成する、画素(点)を近似する方法のタイプを指定する。
56        # 戻り値  ###########################################
57        # self.img : 輪郭付き画像情報
58        # self.contours : 輪郭を形成する画素(点)情報
59        # self.hierarchy : オブジェクト(物体)の階層構造情報
60        ####################################################
61
62        # OpenCVのバージョンが4.0より小さい場合
63        # self.img, self.contours, self.hierarchy = cv2.findContours(self.img, type, cv2.CHAIN_APPROX_NONE)
64        # OpenCVのバージョンが4.0以上の場合
65        self.contours, self.hierarchy = cv2.findContours(self.img, type, cv2.CHAIN_APPROX_NONE)
66
67    # 輪郭の描画を行う関数
68    def exeDrawContours(self):
69        # drawContours : 輪郭の検出情報をもとに、輪郭の描画を行う関数
70        # 第一引数 : 画像情報
71        # 第二引数 : 輪郭を形成する画素(点)情報
72        # 第三引数 : 輪郭を形成する画素(点)のインデックス番号を指定する。例えば0を指定すると、1番目の輪郭を形成する画素(点)のみを描画する。1を指定すると、2番目の輪郭を形成する画素(点)のみを描画する。輪郭を形成する画素(点)を全て描画したい場合は、-1を指定する。
73        # 第四引数 : 輪郭を形成する画素(点)の色。
74        # 第五引数(任意) : 輪郭を形成する画素(点)の大きさを設定。デフォルト1。
75        # 戻り値 : 画像情報
76        # drawContoursについて : https://kuroro.blog/python/xaw33ckABzGLiHDFWC3m/
77        self.img = cv2.drawContours(self.img, self.contours, -1, 192, 5)
78
79    # 画像を保存する関数
80    def writeImg(self, filePath):
81        # imwrite : 画像の保存を行う関数
82        # imwriteについて : https://kuroro.blog/python/i0tNE1Mp8aEz8Z7n6Ggg/
83        # 第一引数 : 保存先の画像ファイル名
84        # 第二引数 : 多次元配列(numpy.ndarray)
85        cv2.imwrite(filePath, self.img)
86
87# imread : 画像ファイルを読み込んで、多次元配列(numpy.ndarray)にする。
88# imreadについて : https://kuroro.blog/python/wqh9VIEmRXS4ZAA7C4wd/
89# 第一引数 : 画像のファイルパス
90ins = Paint(cv2.imread('./xxx.xxx'))
91ins.setGray()
92ins.setBinaryImage()
93
94for type in [cv2.RETR_EXTERNAL, cv2.RETR_LIST, cv2.RETR_CCOMP, cv2.RETR_TREE]:
95    ins.exeFindContours(type)
96    ins.exeDrawContours()
97    ins.writeImg(str(type) + '.xxx')

以下の画像のように、第二引数(mode)へ与えるタイプによって、輪郭の検出に違いが現れます。

元画像

cv2.RETR_EXTERNAL

cv2.RETR_LIST

cv2.RETR_CCOMP

cv2.RETR_TREE

drawContoursに関しては、OpenCVで使われるdrawContoursって?具体的な活用法を徹底解説!?でまとめていますので、詳しく知りたい方は是非ご確認ください。

findContours関数の第三引数(method)で指定される、タイプについて調べてみた

findContours関数の第三引数(method)では、輪郭を形成する、画素(点)を近似する方法のタイプを指定します。

輪郭を形成する、画素(点)を近似する方法のタイプとしては、

タイプ名称特徴
cv2.CHAIN_APPROX_NONE                     輪郭を形成する、すべての画素(点)を利用します。
cv2.CHAIN_APPROX_SIMPLE輪郭を形成する画素(点)を、要所で必要な数だけ利用する。
cv2.CHAIN_APPROX_TC89_L1, cv2.CHAIN_APPROX_TC89_KCOSTeh-Chinチェーン近似アルゴリズムの1つを適用します。

があります。

例えば以下のようなコードを作成すると、

1# cv2(OpenCV)を利用する宣言を行う。
2import cv2
3
4class Paint:
5    # 画像情報
6    img = None
7    # しきい値情報
8    # しきい値とは? : https://wa3.i-3-i.info/word15002.html
9    ret = None
10    # 輪郭を形成する画素(点)情報
11    contours = None
12    # オブジェクト(物体)の階層構造情報
13    hierarchy = None
14
15    # コンストラクタ
16    # コンストラクタとは? : https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF#:~:text=%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%EF%BC%88%E8%8B%B1%3A%20constructor%EF%BC%89%E3%81%AF,%E5%AF%BE%E7%BE%A9%E8%AA%9E%E3%81%AF%E3%83%87%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%80%82
17    # 画像の初期化
18    def __init__(self, image=None):
19        self.img = image
20
21    # 画像をグレースケールに設定する関数
22    # グレースケールとは? : https://www.shinkohsha.co.jp/blog/monochrome-shirokuro-grayscale/
23    def setGray(self):
24        # cvtColor : 画像の色空間(色)の変更を行う関数。
25        # cvtColorについて : https://kuroro.blog/python/7IFCPLA4DzV8nUTchKsb/
26        # 第一引数 : 多次元配列(numpy.ndarray)
27        # 第二引数 : 変更前の画像の色空間(色)と、変更後の画像の色空間(色)を示す定数を設定。
28        # cv2.COLOR_BGR2GRAY : BGR(Blue, Green, Red)形式の色空間(色)を持つ画像をグレースケール画像へ変更する。
29        self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
30
31    # グレースケール画像を二値画像へ変更する関数
32    def setBinaryImage(self):
33        # threshold : しきい値を用いて画像を二値画像へ変更する関数。
34        # thresholdについて : https://kuroro.blog/python/jofbNumJ9HtfTxnM8QHJ/
35
36        # 第一引数 : 多次元配列(numpy.ndarray)
37        # 第二引数 : しきい値。float型。
38
39        # 第三引数 : しきい値を超えた画素に対して、255を設定する。しきい値を超えていないものに関しては、0を与える。
40
41        # 第四引数 : 二値画像を判定する条件のタイプを指定する。
42        # cv2.THRESH_BINARY : (画素 <= 第二引数)の場合、画素に対して、0の値を与える。(画素 > 第二引数)の場合、画素に対して、第三引数の値を与える。
43
44        # 戻り値 #################
45        # self.ret : しきい値を返す。
46        # self.img : 多次元配列(numpy.ndarray)を返す。
47        #########################
48        self.ret, self.img = cv2.threshold(self.img, 160, 255, cv2.THRESH_BINARY)
49
50    # 輪郭の検出を行う関数
51    def exeFindContours(self, type):
52        # findContours : 輪郭の検出を行う関数
53        # 第一引数(thresh) : 二値画像情報
54        # 第二引数(mode) : 輪郭を検出するタイプを指定する。
55        # 第三引数(method) : 輪郭を形成する、画素(点)を近似する方法のタイプを指定する。
56        # 戻り値  ###########################################
57        # self.img : 輪郭付き画像情報
58        # self.contours : 輪郭を形成する画素(点)情報
59        # self.hierarchy : オブジェクト(物体)の階層構造情報
60        ####################################################
61
62        # OpenCVのバージョンが4.0より小さい場合
63        # self.img, self.contours, self.hierarchy = cv2.findContours(self.img, cv2.RETR_EXTERNAL, type)
64        # OpenCVのバージョンが4.0以上の場合
65        self.contours, self.hierarchy = cv2.findContours(self.img, cv2.RETR_EXTERNAL, type)
66
67    # 輪郭の描画を行う関数
68    def exeDrawContours(self):
69        # drawContours : 輪郭の検出情報をもとに、輪郭の描画を行う関数
70        # 第一引数 : 画像情報
71        # 第二引数 : 輪郭を形成する画素(点)情報
72        # 第三引数 : 輪郭を形成する画素(点)のインデックス番号を指定する。例えば0を指定すると、1番目の輪郭を形成する画素(点)のみを描画する。1を指定すると、2番目の輪郭を形成する画素(点)のみを描画する。輪郭を形成する画素(点)を全て描画したい場合は、-1を指定する。
73        # 第四引数 : 輪郭を形成する画素(点)の色。
74        # 第五引数(任意) : 輪郭を形成する画素(点)の大きさを設定。デフォルト1。
75        # 戻り値 : 画像情報
76        # drawContoursについて : https://kuroro.blog/python/xaw33ckABzGLiHDFWC3m/
77        self.img = cv2.drawContours(self.img, self.contours, -1, 192, 5)
78
79    # 画像を保存する関数
80    def writeImg(self, filePath):
81        # imwrite : 画像の保存を行う関数
82        # imwriteについて : https://kuroro.blog/python/i0tNE1Mp8aEz8Z7n6Ggg/
83        # 第一引数 : 保存先の画像ファイル名
84        # 第二引数 : 多次元配列(numpy.ndarray)
85        cv2.imwrite(filePath, self.img)
86
87# imread : 画像ファイルを読み込んで、多次元配列(numpy.ndarray)にする。
88# imreadについて : https://kuroro.blog/python/wqh9VIEmRXS4ZAA7C4wd/
89# 第一引数 : 画像のファイルパス
90ins = Paint(cv2.imread('./xxx.xxx'))
91ins.setGray()
92ins.setBinaryImage()
93
94for type in [cv2.CHAIN_APPROX_NONE, cv2.CHAIN_APPROX_SIMPLE, cv2.CHAIN_APPROX_TC89_L1, cv2.CHAIN_APPROX_TC89_KCOS]:
95    ins.exeFindContours(type)
96    ins.exeDrawContours()
97    ins.writeImg(str(type) + '.xxx')

以下の画像のように、第三引数(method)へ与えるタイプによって、輪郭を形成する、画素(点)を近似する方法に違いが現れます。

元画像

cv2.CHAIN_APPROX_NONE

cv2.CHAIN_APPROX_SIMPLE

cv2.CHAIN_APPROX_TC89_L1

cv2.CHAIN_APPROX_TC89_KCOS

まとめ

  • OpenCVで使われるfindContoursとは、輪郭を検出して、画像内に含まれるオブジェクト(物体)を取得する関数を意味する。
  • 「画像のどの部分を輪郭として検出すべきか」判定できるように、二値画像へ加工する前処理を行う必要がある。

参考文献