OpenCVで輪郭抽出・検出する方法【Python】

プログラミング

この記事で、OpenCVで輪郭抽出・検出する方法を解説しています。
便利な関数がOpenCVには用意されています。

本記事の内容

  • OpenCVで輪郭抽出する環境
  • 【輪郭検出関数】findContours
  • 画像をグレースケール化する
  • findContoursで輪郭抽出を行う
  • 【結論】findContoursに過剰に期待するのはやめておきましょう

それでは、上記に沿って解説していきます。

OpenCVで輪郭抽出する環境

OpenCVは、いろいろな環境で動きます。
いわゆるクロスプラットフォームというモノですね。

対応OS

  • Windows
  • Linux
  • FreeBSD
  • macOS
  • Android
  • iOS

対応言語

  • C/C++
  • Java
  • Python

この記事では、Windows上のPythonで検証を行います。
OenCVのインストールに関しては、次の記事をご覧ください。

準備が整ったら、実際にOpenCVで輪郭検出を行っていきましょう。

【輪郭検出関数】findContours

冒頭で述べたように、OpenCVには便利な関数が用意されています。

findContours

contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

引数

image8ビット,シングルチャンネル(グレースケール)
mode輪郭検索モード
method輪郭近似法
offsetすべての輪郭点をシフトさせるオフセット

戻り値

contours検出された輪郭の座標(配列)
hierarchy検出された輪郭毎の階層構造情報(配列)

正直、説明を見てもよくわからないと思います。
実際に使っているところを確認しましょう。

ただ、imageのグレースケールには注意ですね。
用意する画像は、グレースケールで読み込んでおく必要があります。

輪郭抽出の前に、画像をグレースケール化することをプログラムで確認しておきましょう。

画像をグレースケール化する

対象画像は、わかりやすいモノとして以下を用意。

test.png

この画像をグレースケール化します。
そのためのプログラムコードは以下。

import cv2

# 画像読み込み
img = cv2.imread("./img/test.png")

# グレースケール化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 画像保存
cv2.imwrite("./img/test_gray.png", img_gray)

imgフォルダにあるtest.pngを読み込みます。
読み込んだ画像をグレースケール化。
そして、グレースケール化した画像をファイルに保存。

このプログラムを実行すると、次の画像が作成できました。

test_gray.png

見事にグレースケール化されています。
グレースケール化に関しても理解できたので、輪郭抽出を検証します。

findContoursで輪郭抽出を行う

最初に、輪郭抽出した画像を表示します。

test_contours.png

上手く輪郭抽出できていますね。
ただし、ここまでやるのに苦労しました。
苦労したコードは、以下。

import cv2

# 画像読み込み
img = cv2.imread("./img/test.png")

# グレースケール化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray = cv2.GaussianBlur(img_gray, (7, 7), 0)
img_gray= cv2.medianBlur(img_gray, 1)   

# 輪郭を抽出する
ret,thresh = cv2.threshold(img_gray, 230, 255,0)  
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

# 読み込んだ画像に輪郭を描画
img_contours = cv2.drawContours(img, contours, -1, (225, 0 ,225), 3)

# 輪郭を描画した画像をファイルに保存
cv2.imwrite("./img/test_contours.png", img_contours)

見てウンザリしますよね。
メソッドがたくさん出てきます。

各メソッドのパラメータを試行錯誤して調整しました。
それが上記の結果です。

最後に、今回の記事のまとめをしておきます。

【結論】findContoursに過剰に期待するのはやめておきましょう

結論は、そのままです。
「findContoursに過剰に期待はするな」です。

私は、findContoursにはもっと期待していました。
画像を読み込ませれば、さくっと輪郭抽出ができるモノだと。

しかし、現実はそんなに甘くありませんでした。
まずは、その現実を一緒に確認しましょう。

別の画像を読み込むと、途端に上記コードは動きません。
例えば、以下の画像です。

読み込み画像

輪郭抽出した画像

全く機能していません。
おそらく、各メソッドのパラメータを調整すれば、輪郭抽出をすることでしょう。

しかし、画像毎にパラメータを調整するのはやってられません。
バカらしいです。

もちろん、私のコードがよろしくないのは理解しています。
もっといいコーディングはあるのかもしれません。

そうだとしても、findContoursには期待過剰でした。
正確には、findContoursはちゃんと仕事をしているのかもしれません。

ただ、findContoursに渡す画像を用意するのが、あまりも大変です。
それもひっくるめてfindContoursには、期待を裏切られました。
利用する状況次第なのでしょうね、その効果は。

もし、この記事を読んでfindContoursを弁護する方がいれば、是非とも意見をお願いします。
問い合わせフォームからでも、Twitterからでもお願いします。

タイトルとURLをコピーしました