つれづれなる備忘録

日々の発見をあるがままに綴る

OpenCVの使い方24 ~ 輪郭抽出

 今回はOpenCVを用いて画像の輪郭を抽出する方法について紹介したい。

1. 画像読み込みとバイナリ画像変換

 今回は以下のOpenCVのモノクロロゴ画像を用いて輪郭線を抽出する。

"Open CVモノクロロゴ"
Open CVモノクロロゴ

以下のようにGoogle Colabを用いてモノクロ画像をgrayとして読み込む。

import cv2
import numpy as np
from matplotlib import pyplot as plt

from google.colab import files
uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)

輪郭抽出処理を行うため、モノクロ画像をcv2.thresholdを用いて255と0のバイナリ画像に変換しthreshに格納する。

ret,thresh = cv2.threshold(gray,127,255,0)
plt.imshow(thresh,cmap='gray')

もともと単純な画像なので、バイナリ画像に変換しても元の画像とあまり変化はない。

"バイナリ画像"
バイナリ画像

2. 輪郭抽出

 バイナリ画像から輪郭線を抽出するにはcv2.findContoursを使用する。引数はバイナリ画像、輪郭検出方法、輪郭近似方法を指定し、出力は検出した輪郭(リスト)と輪郭の階層情報となる。具体的には以下のようにして、contoursに輪郭をリストとして格納する。

contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

輪郭検出方法は次の4種類:cv2.RETR_EXTERNAL, cv2.RETR_LIST, cv2.RETR_CCOMP, cv2.RETR_TREEが指定できるが、今回はcv2.RETR_TREEを用いる。 また輪郭近似方法はすべての輪郭点を格納するcv2.CHAIN_APPROX_NONEと水平・垂直・斜めの線分を圧縮し,それらの端点のみを残すcv2.CHAIN_APPROX_SIMPLEがあるがcv2.CHAIN_APPROX_SIMPLEを使用する。

3. 抽出した輪郭の描画

 抽出した輪郭を描画するには、cv2.drawContoursを使用する。元のバイナリ画像と同サイズの配列(ゼロ行列)を用意しcnt_imgとする。 cv2.drawContoursの引数は、配列、検出輪郭リスト、輪郭リストの番号、輪郭を描画する色、輪郭線の太さを指定する。 輪郭リストの番号は-1とするとすべての輪郭が描画される。またモノクロ画像であれば描画する色は255とすると白線になる。カラーの場合はRGBを(0,255,0)のように指定する。

cnt_img = np.zeros_like(thresh, dtype=np.uint8) 
cnt_img = cv2.drawContours(cnt_img, contours, -1, 255, 2)
plt.imshow(cnt_img,cmap='gray')

以下OpenCVの輪郭が抽出できていることがわかる。

"全輪郭の描画"
全輪郭の描画

輪郭リストの番号を1つ指定すると、それに対応した輪郭線のみが描画される。例えば輪郭リストを2と指定するとeの内側のみの輪郭が描画される。

cnt_img = np.zeros_like(thresh, dtype=np.uint8) 
cnt_img = cv2.drawContours(cnt_img, contours,2, 255, 2)
plt.imshow(cnt_img,cmap='gray')

"輪郭の描画"
輪郭の描画

抽出された輪郭の数を調べるにはlen()を用いてリストの長さを取得することで得られる。今回は12個の輪郭が抽出されていることがわかる。

n=len(contours)
n
>12

輪郭リストの各番号がどの輪郭に対応しているか、繰り返し処理を行って表示させることができる。 サブプロットの各タイトルにリストの番号を示すようにしている。

plt.figure(figsize=(12,12))
for i in np.arange(n):
  cnt_img = np.zeros_like(thresh, dtype=np.uint8) 
  cnt_img = cv2.drawContours(cnt_img, contours,i, 255, 2)
  plt.subplot(3,4,i+1)
  plt.imshow(cnt_img,cmap='gray')
  plt.title('Cont Num'+str(i))

"各輪郭の描画"
各輪郭の描画

4. まとめ

 今回はOpenCVによるモノクロ画像の輪郭の抽出と描画する方法について紹介した。