python: OpenCVで画像から輪郭を検出する

今回取り出す画像 プログラミング
スポンサーリンク




こんにちは、おみです。

今回は、opencvを使用して、サムネイルの画像から輪郭を検出したいと思います。

ちなみに、画像から輪郭を検出する処理のことを「エッジ検出」と言います。

スポンサーリンク

手順

今回は、下記の手順で輪郭を抽出します。

  1. 画像の読み込み
  2. 画像をモノクロに変換する
  3. 画像をぼかす
  4. 2値化する(画像を白と黒だけで表す
  5. 輪郭を検出する
  6. 輪郭を描画する

手順1 画像の読み込み

輪郭の検出を行いたい画像ファイルを読み込みます。

# 画像のパスを指定
file_path = "bird.png"

# 画像が存在するかを確認
if not os.path.exists(file_path):
    print("画像が存在しません。")

# 画像を読み込む
img = cv2.imread(file_path)

ファイルを読み込む処理全般に言えることですが、ファイルが本当に存在するかをプログラムでも確認しましょう。

「os.path.exists(ファイルパス)」で、ファイルが存在するかを判定できます。

 

↓読み込んだ画像

手順2 画像をモノクロに変換する

読み込んだ画像を、モノクロに変換します。ちなみにモノクロのことを画像処理ではグレースケールといい、こちらで表記されることが一般的です。

グレースケールに変換することにより、計算量を削減することができます。

# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cv2.imreadで読み込んだ画像は「BGR形式」で描画されており、これをグレースケールに変換するためにcv2.cvtColorの第2引数に「cv2.COLOR_BGR2GRAY」を指定します。

↓グレースケールに変換した画像

手順3画像をぼかす

グレースケールの画像を、ぼかします。

ぼかすことで、画像中にあるノイズ(不要な細かい点等)を除去することができます。

# ガウシアンフィルターをかける
gauss = cv2.GaussianBlur(gray, (5, 5), 0)

今回は、ガウシアンフィルターを使って画像をぼかします。

ガウシアンフィルターがどのようにして画像をぼかすかの説明は割愛します。興味がある方は調べてみてください。

↓ぼかした画像

手順2の画像と比べると、鳥の羽のあたりなどがわかりやすくぼかされているかと思います。

手順4 2値化する

画像の2値化を行います。

2値化を行うことにより、さらに計算量を削減することができます。

# 2値化する
thres = cv2.threshold(gauss, 140, 255, cv2.THRESH_BINARY)[1]

↓2値化を行った画像

色の濃い部分は黒、薄い部分は白で表されます。

手順5 輪郭を検出する

2値化した画像のうち、黒部分の輪郭を検出します。

# 輪郭のみを抽出する
cons = cv2.findContours(thres,
                        cv2.RETR_LIST,
                        cv2.CHAIN_APPROX_NONE)[0]

手順6 輪郭を描画する

検出した画像の輪郭を、読み込んだ画像に描画します。

# 輪郭を描画する
for con in cons:
# 面積が閾値を超えない場合、輪郭としない
if cv2.contourArea(con) < 100:
continue

# 輪郭を描画する
cv2.polylines(img, con, True, (255, 0, 0), 5)

↓輪郭を描画した画像

ソースコード

ここまでの手順をまとめて、1つのプログラムにしたものがこちらです。

import cv2
import math
import os

# 画像のパスを指定
file_path = "bird.png"

# 画像が存在するかを確認
if not os.path.exists(file_path):
    print("画像が存在しません。")

# 画像を読み込む
img = cv2.imread(file_path)

# 画像のサイズを変更(別に書かなくても良い)
height, width = img.shape[:2]
img = cv2.resize(img, (math.floor(width / 2), math.floor(height / 2)))

# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ガウシアンフィルターをかける
gauss = cv2.GaussianBlur(gray, (5, 5), 0)

# 2値化する
thres = cv2.threshold(gauss, 140, 255, cv2.THRESH_BINARY)[1]

# 輪郭のみを検出する
cons = cv2.findContours(thres,
                        cv2.RETR_LIST,
                        cv2.CHAIN_APPROX_NONE)[0]

# 輪郭を描画する
for con in cons:
    # 面積が閾値を超えない場合、輪郭としない
    if cv2.contourArea(con) < 100:
        continue

    # 描画処理
    cv2.polylines(img, con, True, (255, 0, 0), 5)

# 画像を表示する
cv2.imshow("result", img)

cv2.waitKey(0)
cv2.destroyAllWindows()

↓実行結果

 

改善点

とりあえずBGR形式のまま2値化を行っているので、綺麗に輪郭を検出できているわけではありません。

これをどのようにして綺麗に検出できるようにするかが、プログラマーの腕の見せどころです。

 

 

 

 

コメント

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