🐍

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

2021/12/18に公開

OpenCVで画像から輪郭を検出したいと思います。
ちなみに画像から輪郭を検出する処理のことをエッジ検出と言います。

手順

  1. 画像を読み込む
  2. 画像をモノクロに変換する
  3. 画像をぼかす
  4. 二値化する
  5. 輪郭を検出する
  6. 輪郭を描画する

全体像

全体のソースコードはこちらです。

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()

画像の読み込み

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

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

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

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

↓読み込んだ画像

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

画像処理の世界ではモノクロのことをグレースケールといいます。
これにより、計算量を削減することができます。

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

cv2.imreadで読み込んだ画像は「BGR形式」で描画されており、これをグレースケールに変換するためにcv2.cvtColorの第2引数に「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)

↓輪郭を描画した画像

改善点

BGR形式のまま二値化を行っているので、綺麗に輪郭を検出できているわけではありません。
これをどのようにして綺麗に検出できるようにするかが、プログラマーの腕の見せどころです。

GitHubで編集を提案

Discussion