OpenCVで輪郭抽出をしたいけど、上手くいきません…
OpenCVで輪郭抽出をするときは画像の2値化がポイントだ。
✔️ 本記事のテーマ
OpenCVで上手く輪郭抽出する方法
✔️ 読者さんへの前置きメッセージ
結論から述べると、
以下の関数で輪郭抽出を行うことができます。
戻り値 = cv2.findContours(白黒画像, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
本記事は「OpenCVで輪郭抽出する方法とコツ」について書いています。
この記事を読むことで
「OpenCVで輪郭抽出するサンプルコード や 輪郭抽出する際のポイント」
について理解できます。
OpenCVの輪郭抽出はコード自体は簡単ですが、
いくつかのポイントに気をつけないと関数がエラーになってしまったり、
きれいな輪郭にならないでしょう。
本記事では、そんなOpenCVの輪郭抽出の方法とそのコツについて解説していきます。
では、解説していきましょう。
OpenCVの輪郭抽出の流れ
OpenCVの輪郭抽出は以下の流れで実施します。
これを理解していると、理解がスムーズになるので、頭に入れておきましょう。
- 画像を読み込む
- 画像のグレースケール化
- 画像の白黒2値化
- 輪郭を抽出する
- 輪郭を画像に書き込む
輪郭抽出までに「グレースケール化」「白黒2値化」という前処理が入っています。
この前処理が大切です。
特に「3.画像の白黒2値化」がきれいな輪郭を抽出するポイントです。(後で説明します)
OpenCVで輪郭抽出する方法
では、実際にOpenCVで輪郭抽出をする方法をコードで解説していきましょう。
具体的なコードとしては以下のようになります。
import numpy as np
import cv2
# 画像を読み込む
img = cv2.imread('./sample.jpg')
# 画像のグレースケール化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 画像の白黒2値化
ret, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
# 輪郭を抽出する
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 輪郭を画像に書き込む
output = cv2.drawContours(img, contours, -1, (0,255,0), 3)
各処理ごとに解説していきましょう。
画像を読み込む
OpenCVで画像処理を行うときには対象画像をOpenCVで読み込む必要があります。
img = cv2.imread('./sample.jpg')
このようにして画像を読み込んで画像データにしておきましょう。
画像のグレースケール化
次に読み込んだ画像をグレースケール化しましょう。
グレースケールとは画像をグレーの濃淡で表現することです。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.cvtColor()を使うことで、
先ほど読み込んだ画像をグレースケールにすることができます。
画像の白黒2値化
さらにグレースケール画像を白黒2値化していきます。
先ほどのグレースケール画像は0~255のグレーの濃淡で表現されています。
それを0(白)か255(黒)にすることで白黒画像に変換します。
ret, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
画像を白黒2値化することで、輪郭抽出に必要な前処理が完了します。
ちなみに、第2引数として与えた数字(=120)が閾値になります。
今回は120以上のグレーを255(黒)に変換しています。
この120という数字を変えることで出力される画像が変わってきます。
輪郭を抽出する
ここまで前処理が完了していれば、いよいよ輪郭抽出を行います。
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
輪郭抽出にはcv2.findContours()を使います。
第1引数が「入力画像(=白黒画像)」、
第2引数が「輪郭抽出モード」、
第3引数が「輪郭の近似手法」となっています。
一応、「輪郭抽出モード」「輪郭の近似手法」はそれぞれ以下の値を指定することができますが、よく分からなければsample codeのままで良いかと思います。
輪郭抽出モード:
- CV_RETR_EXTERNAL:最も外側の輪郭のみを抽出する
- CV_RETR_LIST:すべての輪郭を抽出し、一切の階層構造を保持しない
- CV_RETR_CCOMP:すべての輪郭を抽出し、それらを2階層構造として保持する
- CV_RETR_TREE すべての輪郭を抽出し、入れ子構造になった輪郭を完全に表現する階層構造として保持する
輪郭の近似手法:
- CV_CHAIN_APPROX_NONE:すべての輪郭点を完全に格納する
- CV_CHAIN_APPROX_SIMPLE:水平・垂直・斜めの線分を圧縮し,それらの端点のみを残す
- CV_CHAIN_APPROX_TC89_L1:Teh-Chinチェーン近似アルゴリズムの1つを適用する
- CV_CHAIN_APPROX_TC89_KCOS:上に同じ
輪郭を画像に書き込む
先ほど抽出した輪郭(=contours)を画像に書き込みます。
output = cv2.drawContours(img, contours, -1, (0,255,0), 3)
- 第1引数が出力画像(=カラー画像)、
- 第2引数が輪郭データ、
- 第3引数が輪郭線の閾値、
- 第4引数が輪郭線の色、
- 第5引数が輪郭線の太さ
となっています。
引数が多くてややこしいですが、以下のことを守れば大体OKです。
第1引数(=出力画像)で指定した画像に書き込みを行うので、
白黒画像ではなくカラー画像を指定しましょう。
第2引数(= 輪郭データ)は、先ほどのcv2.findContours()の戻り値をそのまま使います。
第3引数(=輪郭線の閾値)は、負の値を指定することで全ての輪郭線を書き込みます。
ここは負の値(例えば、-1)固定で良いと思います。
書き込む輪郭を調整したいなら、前段階の「白黒2値化」でやった方が良いと思います。
第4引数(=輪郭線の色)は、色をRGB値で指定します。
(255, 0, 0)なら赤、(0, 255, 0)なら黄緑、(0, 0, 255)なら青になります。
第5引数(=輪郭線の太さ)は、線の太さを数値で指定すればOKです。
なお、この数値を負の値にすると、輪郭内部が塗り潰されます。
OpenCVで輪郭抽出を行う際のポイント
ここからは綺麗な輪郭を抽出をするポイントを解説していきます。
ポイントは以下の3つです。
前処理として入力画像の白黒2値化をする
白黒二値化のパラメータを調節する
findContours()の第2引数、第3引数は固定
順番に解説していきましょう。
前処理として入力画像の白黒2値化をする
まず、輪郭抽出を行うcv2.findContours()の入力画像には白黒画像を指定しましょう。
これをしないとそもそもcv2.findContours()がうまく動きません。
カラー画像 → グレースケール画像 → 白黒画像
という順番で入力画像に前処理を加えてやることで輪郭抽出ができるようになります。
白黒二値化のパラメータを調節する
ここがきれいな輪郭を抽出するための肝になります。
ret,thresh = cv2.threshold(gray,120,255,cv2.THRESH_BINARY)
このcv2.threshold()の第2引数(=この例では120)で閾値を指定しています。
つまりここで「0~255のグレーの濃淡の120以上を黒(255)とする白黒画像にする」と設定しています。
そもそも白黒画像にした時点で上手く輪郭が出ていないと、
cv2.findContours()で輪郭は検出できません。
以下のように輪郭が出ていればOKです。
OK画像例:
逆に、白黒2値化した時点で白飛びしていたり、
輪郭が潰れている画像ではきれいな輪郭は検出できないでしょう。
NG画像例:
きれいな輪郭を検出できていない場合は一度、白黒画像を出力してみて、
上手く白黒2値化できているか確認してみましょう。
findContours()の第2引数、第3引数は固定
cv2.findContours()の第2引数「輪郭抽出モード」、第3引数「輪郭の近似手法」は
なるべく固定した方が良いでしょう。
ここで抽出する輪郭を調整しようとするときっと訳が分からなくなります。
この引数で調整するには、
OpenCVの理解だけでなく、画像処理の理論レベルで理解する必要があります。
抽出する輪郭を調整したいのであれば、
既に述べたように白黒2値化するときに調整した方が合理的です。
OpenCVやPythonについてもっとスキルをつけるなら
今回はOpenCVで画像を回転させる方法とその他の関数を一部解説しました。
OpenCVは注目が集まっていて、いま需要の高い技術です。
もし、OpenCVについて独学でスキルをつけるなら、以下の書籍がオススメです。
この書籍はOpenCVの基礎から応用までを
丁寧にかつ詳細に解説しています。
OpenCVのほぼ全てを網羅しているとも言えるほどの徹底ぶりなので、
関数のリファレンスとしても使用することができます。
本記事で解説した輪郭抽出についても掲載されています。
コメント