48
44

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 5 years have passed since last update.

[OpenCV][Python3]検出した輪郭を描画し、輪郭線を近似して滑らかにする

Last updated at Posted at 2018-10-17

はじめに

輪郭が滑らかな物体に関しては、輪郭検出処理のみでも十分滑らかな輪郭が検出できる場合もある。しかしながら今回は、複雑な輪郭を検出し、近似して滑らかな輪郭にする必要があった。
そこで、OpenCV、Pythonを使用して被写体の輪郭線を描画後に近似している記事が見当たらなかったため記事にしちゃった。

環境

Python 3.7.0
opencv-python 3.4.3.18
numpy 1.15.2

処理の流れ

  1. 元画像の読み込み
  2. HSV色空間に変換
  3. H、S、Vそれぞれの要素のみの画像を取得
  4. 上記の使えそうな画像のヒストグラムを平坦化
  5. 二値化
  6. モーフィング処理(今回はクロージング)
  7. 輪郭検出
  8. 輪郭近似
  9. 輪郭描画

本記事ではOpenCVの基本的な使い方の部分に関しては解説していません。
特に「8. 輪郭近似」に関して詳しく書きます。
その他の処理は他の記事などを参考にしてください。
また、一通りの処理を実装したプログラムを下記にアップロードしています。参考になれば。(途中でコメントを英語にするのが面倒になったため半端に英語です:cloud_lightning:
Github:https://github.com/yoshihiroKani/ContourDetectionAndApproximation/tree/master

本題

まず元画像はこれです。

雲の輪郭を取得していきます。複雑な輪郭なのでやり甲斐がありそうですね。
それではやっていきましょ~

「処理の流れ - 3」
H、S、Vそれぞれの要素のみの画像を取得

H、S、Vの画像はそれぞれ以下のようになりました。

H

S

V
v_screenshot_16.10.2018.png

「処理の流れ - 4」
上記の使えそうな画像のヒストグラムを平坦化

これを実施すると場合によりますが輪郭が更にクッキリします。
hist_s_img_screenshot_16.10.2018.png

「処理の流れ - 5」
二値化

result_bin_screenshot_16.10.2018.png
(画像の枠は付けるのが面倒なので勘弁)

「処理の流れ - 6」
モーフィング処理(今回はクロージング)

result_morphing_screenshot_16.10.2018.png
今回は画像で分かりやすいようにフィルタの値を大きくしています。実際はフィルタの形や大きさを変えたり、何度か繰り返しかけて見てくださいね。フィルタの大きさに関してはGithubの下記のコードの(10, 10)の部分を調整してください。形はMORPH_RECTこれを別の変数に変えてね。(ちな、OpenCV初心者だと分かりづらいけどMORPH_RECTこれはOpenCVに標準で用意されている定数です。あとはググって。)

## フィルタの設定
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))

「処理の流れ - 7」
輪郭検出

findContours()を使用しましょう。
今回は下記のようにしてます。

# 輪郭検出
tmp_img, contours, _ = cv2.findContours(
    result_morphing, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

アプローチの方法は簡単に思いつく限りでも3通りありました。

  1. モーフィング処理を行う。(上記「6. モーフィング処理(今回はクロージング)」にて既出)
  2. 輪郭線を近似する。
  3. 輪郭線の描画の太さを太くするw

#### 1. モーフィング処理を行う
モーフィング処理の仕方でも輪郭の滑らかさは変わります。例えば、「20x20の矩形フィルタをかける場合」と、「20x20の十字形フィルタをかける場合」を画像で比較したものが以下です。(因みに、赤枠の辺りを拡大しています。)

(拡大部分)

20x20の矩形フィルタの結果
2020rect.png

20x20の十字形フィルタの結果
2020cross.png

一長一短ですね。「雲の大まかな形を滑らかな輪郭で取りたい」場合は上、「雲の形をできる限り詳細に取りたい」場合は下といった感じでしょうか。
因みに、今回の被写体である雲は輪郭がクッキリしていないため分かりづらいですが(「分かりづらいですが」じゃねえわじゃあ何でこれにした)、雲の薄い部分まで取りたい場合は下記の二値化の閾値(threshold()の第二引数)を調整しましょう。(今は200なので、HSVのSのみのグレースケール画像の200以上のピクセルを雲として処理しています。)

# 二値化
_, result_bin = cv2.threshold(
    s_img, 200, 255, cv2.THRESH_BINARY)

更に下の画像のジャギーを減らすために2x2の矩形フィルタをかけてみました。

20x20の十字形フィルタ→2x2の矩形フィルタの結果
2020cross_22rect.png

んー…?内側の輪郭が取れるようになっていますが、ディテールが失われている部分もありますね。
まあまあ。手法の紹介にはなったかな(?)。
因みに、ある領域の内側の輪郭を取るかどうかの設定は以下のRETR_TREE部分でしています。

# 輪郭検出
tmp_img, contours, _ = cv2.findContours(
    result_morphing, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

ここが分かりやすい。
参考:https://qiita.com/anyamaru/items/fd3d894966a98098376c

#### 2. 輪郭線を近似する
今回のメインはこれ。上記「1. モーフィング処理を行う」で取り上げた「20x20の十字形フィルタをかける場合」の画像の輪郭線を近似してみましょう。
該当するソースコードは以下。

epsilon = 0.0001*cv2.arcLength(cnt,True)
approx.append(cv2.approxPolyDP(cnt,epsilon,True))

epsilonは「実際の輪郭と近似輪郭の最大距離を表し,近似の精度を表すパラメータ」(下記参考サイトより引用)

参考:http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html

epsilon=0.00005の場合
approx.png

epsilon=0.0001の場合
approx_0.0001.png

ん?滑らか?滑らかとは?
曲線近似する方法もあるかも。もし曲線近似する方法を見つけた、知ってるという優しい天才イケメンがいたらどうかコメントで教えてください。
今回の方法は、大まかな形が直線的なではあるものの輪郭が入り組んでいるもの(川とか道路とか)を直線に近似したい時には使えそうですね。

#### 3. 輪郭線の描画の太さを太くする
これは輪郭線のデータ自体はいじりません。ただ描画する線を太くするだけですが、細い線より太い線の方が当然滑らかには見える…はず。drawContours()の最後の引数が線の太さです。

cv2.drawContours(cp_org_img_for_draw, large_contours, -1, (255, 0, 0), 1)

これ以前の画像は全て1で設定した結果です。
試しに上記「epsilon=0.00005の場合」の画像をベースに4くらいに変えてみます。
line_4.png

…線の角は取れていますが、当然ディテールは潰れるところもあります。

まとめ

今回は境界線を取得するにあたって、なぜか境界線が曖昧な画像を選択してしまいましたが、もっと空と雲の境界を明確に取得したい場合は閾値の設定を変更すれば対応できると思います。

以上です。

48
44
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
48
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?