輪郭: 初めの一歩

目的

  • 輪郭とは何なのか理解します
  • 輪郭の検出方法,描画方法を学びます
  • 以下の関数の使い方を学びます : cv2.findContours(), cv2.drawContours()

輪郭とは何か?

輪郭とは同じ色や値を持つ(境界線に沿った)連続する点をつなげて形成される曲線のことです.形状解析や物体追跡,認識お有用なツールになります.

  • 精度よく輪郭を検出するために,二値日画像を使います.そのため,まずはじめにしきい値処理やCannyのエッジ検出などで二値画像を取得します.
  • cv2.findContours() 関数は入力画像を変える処理です.輪郭検出後の処理で入力画像を使用する必要がある場合は,別の変数としてコピーしてください.
  • OpenCVの輪郭検出は,黒い背景から白い物体の輪郭を検出すると仮定しています.物体は白(明るい色),背景は黒(暗い色)と覚えておいてください.

それでは二値画像から輪郭を検出する方法を見て行きましょう:

import numpy as np
import cv2

im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

cv2.findContours() 関数は3個の引数を必要とする関数です.第1引数は入力画像,第2引数はcontour retrieval mode,第3引数は輪郭検出方法を指定するフラグです.出力は輪郭画像,輪郭,輪郭の階層情報です. 輪郭 は検出された全輪郭をPythonのlistとして出力されます.list内の各輪郭は輪郭上の点の(x,y)座標をNumpyのarrayとして保存されています.

Note

第2引数と第3引数及び輪郭の階層情報については後述します.それまではサンプルコードで指定しているフラグや値を使えば輪郭がうまく検出できると思ってください.

輪郭を描画する方法

検出した輪郭を描画するには cv2.drawContours 関数を使います.この関数は境界上に点を持つ形状であれば,輪郭以外の形状の描画にも使えます.第1引数は入力画像,第2引数はPythonのlistとして保存されている輪郭,第3引数は描画したい輪郭のインデックス(第2引数で与えた輪郭のlistから一つの輪郭だけを描画する時に描画したい輪郭の指定に使います.全輪郭を描画する時はー1を指定します.),以降の引数は輪郭を描画する色や線の太さといった情報です.

画像中の全輪郭を描画するには:

img = cv2.drawContours(img, contours, -1, (0,255,0), 3)

画像中の輪郭の内,一つだけ(ここでは4番目の輪郭)を描画するには:

img = cv2.drawContours(img, contours, 3, (0,255,0), 3)

よく使うのは以下の方法です:

cnt = contours[4]
img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)

Note

2番目と3番目の描画方法は同じように見えますが,後で3番目の方法が便利だと分かるようになります.

輪郭の近似方法

これは cv2.findContours 関数の第3引数のことですが,具体的に何を指しているのでしょうか?

前述しましたが,輪郭とは同じ値を持つ形状の境界を指し,境界線上の点の(x,y)座標をデータとして格納しています.しかし,全ての点の座標を保存しておく必要はあるでしょうか?これは輪郭の近似方法によって指定されます.

近似方法のフラグを cv2.CHAIN_APPROX_NONE と指定すると輪郭上の全点の情報を保持します.しかし,本当に全点の情報を保持する必要があるでしょうか?例えば,直線の輪郭を検出したとして,検出した線を表現するのに境界上の全ての点の情報を保持する必要があるでしょうか?いいえ,直線の端点のみを保持するだけで十分でしょう. cv2.CHAIN_APPROX_SIMPLE フラグを指定すると,輪郭を圧縮して冗長な点の情報を削除し,メモリの使用を抑えられます.

以下の例は長方形が描いてある画像に対して近似した輪郭の検出を行います.arrayとして保存している輪郭内の全点を青い円として描画します.最初の画像は cv2.CHAIN_APPROX_NONE フラグを指定して検出した点(734個),2枚目の画像は cv2.CHAIN_APPROX_SIMPLE フラグを指定して検出した点(たったの4個)です.これで近似方法によってどれだけメモリ消費を抑えられるか分かりますよね!!!

Contour Retrieval Method

補足資料

課題