オープンソースの画像処理ライブラリOpenCVを用いて、図形の輪郭を扱う方法を紹介します。
使用するライブラリと図形
はじめに使用するライブラリをimportします。
import numpy as np # おなじみのnumpy
import cv2 # メインの画像処理ライブラリ
from PIL import Image # 画像を簡易に確認するために利用
次に、この記事ないで輪郭を取得する適当な図形を用意します。
# 適当な図形を用意
campus = np.zeros((300,500),dtype=np.uint8) # 図形を出力するキャンパスを用意
campus[100:150,50:250] = 255
campus[50:250,300:400] = 255
Image.fromarray(campus) # キャンパスを出力して確認
この図形を用いて順番に輪郭を取得する手順を追っていきます。
図形の2値化: cv2.threshold
輪郭の取得に先立って、図形を2値化します。
これは、輪郭を取得する関数cv2.findContoursが図形を2値画像として解釈するため、2値化しないと期待と結果が異なってしまうためです。
cv2.thresholdの具体例
図形を2値かするには、cv2.thresholdを用います。
_, campus = cv2.threshold(campus,127,255,cv2.THRESH_BINARY)
図形campusに対して、127以下の値を0へ、127より大きい値を255に変換しています。
cv2.thresholdの使い方
- 引数
- image: 2値化する画像のarray。
- thresh: 2値化する閾値。uint8の画像を中心の値で2値化する場合は127にする。
- maxval: 2値化後の最大値。uint8の場合は255
- type: 2値化のタイプ。単純に2値化する場合はcv2.THRESH_BINARYを指定する。それ以外の2値化タイプはリファレンスを参照。
- 返り値
- retval: 大津の2値化を用いた場合の閾値。通常の2値化では使用しない。
- dst: 2値化後の画像
図形の輪郭を取得: cv2.findContours
図形の輪郭を取得するには、cv2.findContoursを用います。
先ほど2値化した図形に対してcv2.findContoursの実行結果を見ていきます。
cv2.findContoursの具体例
輪郭を取得する図形として、シンプルに2つの直方体を用います。
この図形に対してcv2.findContoursで輪郭を取得した結果は次の通りです。
# findContoursでキャンパスcampusにある図形の輪郭を取得
contours, _ = cv2.findContours(campus, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(contours)
""" 取得した輪郭(contours)
[array([[[ 50, 100]],
[[ 50, 149]],
[[249, 149]],
[[249, 100]]], dtype=int32),
array([[[300, 50]],
[[300, 249]],
[[399, 249]],
[[399, 50]]], dtype=int32)]
"""
2つのnumpy配列が取得されており、それぞれキャンパスにある四角形に対応した座標4点が取得できています。
引数に指定したcv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLEは、それぞれ輪郭の検索方法と、輪郭の近似方法を指定しています。詳細は次節で紹介します。
cv2.findContoursの使い方
cv2.findContoursは基本的に3つの引数(image, mode, method)を受け取り、2つの返り値(contours, hierarchy)を返します。
- 引数
- 返り値
- contours: 検出した全ての輪郭をリストで保持する。各輪郭のshape: [N,1,2]. N: 輪郭の頂点数
- hierarchy: 輪郭の階層情報。オプション。
詳細についてもっと理解したい場合は、以下の公式リファレンスも参照ください。
輪郭から図形を描画: cv2.drawContours
輪郭の座標から図形を描画するには、cv2.drawContoursを用います。
先の節でcv2.findContoursによって取得した輪郭を新しいキャンパス(draw_campus)に描画します。
cv2.drawContoursの具体例
draw_campus = np.zeros_like(campus, dtype=np.uint8) # 新しいキャンパスを用意
cv2.drawContours(draw_campus, contours, -1, color=255, thickness=2)
Image.fromarray(draw_campus) # キャンパスを出力して確認
輪郭の各座標を結んだ図形が描画されました。
cv2.drawContoursでは図形を塗り潰すことはできないので、塗りつぶしたい場合は後述するcv2.fillPolyを用います。
この例では、描画する輪郭をcv2.findContoursで取得しましたが、オリジナルの輪郭を描画したい場合(例えば物体検出タスクでBounding Boxが与えられていて、マスク画像を作りたい場合など)は、輪郭のshapeをOpenCVの仕様と合わせる必要があるので、注意してください。
輪郭の仕様は次節で紹介しています。
cv2.drawContoursの使い方
cv2.drawContoursでは基本的に4つの引数(image, contours, contourIdx, color)を受け取り、1つの返り値(image)を返します。ただし、返り値は入力のimageと同じアドレスなので、用途によっては省略できます。
- 引数
- image: 描画する輪郭を出力するarray。shape: [H,W]もしくは[H,W,3]
- contours: 描画する輪郭。各輪郭をpythonのリストとして複数の輪郭を渡す。shape: list of array([N, 1, 2])、N: 輪郭の頂点数。例: [array([[[ 50, 100]], [[ 50, 149]], [[249, 149]], [[249, 100]]])]
- contourIdx: 描画する輪郭をindexで指定。マイナスを指定すると全ての輪郭が描画される。
- color: 色(白黒もしくはRGB)
- thickness: オプション。線の太さを指定。
- 返り値
- image: 引数として指定したimageと同じアドレスを持つarray
図形を塗りつぶす:cv2.fillPoly
cv2.drawContoursを用いると輪郭を結んだ図形を得られましたが、図形を塗り潰したい場合はcv2.fillPolyを用います。
cv2.fillPolyの具体例
draw_campus = np.zeros_like(campus, dtype=np.uint8)
cv2.fillPoly(draw_campus, contours, 255)
Image.fromarray(draw_campus)
輪郭を塗りつぶした画像を得ることができました。
cv2.fillPolyの使い方
cv2.fillPolyでは3つの必須引数(image, contours, color)を取り、輪郭を塗りつぶした画像imageを返します。ただし、返り値はcv2.drawContours同様に引数のimageと同じなので用途によっては省略できます。
- 引数
- image: 塗りつぶした図形を出力するarray。shape: [H,W]もしくは[H,W,3]
- contours: 描画する輪郭。各輪郭をpythonのリストとして複数の輪郭を渡す。shape: list of array([N, 1, 2])、N: 輪郭の頂点数。例: [array([[[ 50, 100]], [[ 50, 149]], [[249, 149]], [[249, 100]]])]
- color: 色(白黒もしくはRGB)
- 返り値
- image: 引数として指定したimageと同じアドレスを持つarray
終わりに
画像処理のライブラリOpenCVを用いた図形の輪郭操作について紹介しました。
今回は基本的な操作だけですが、輪郭の近似の仕方や、凸包を用いた平坦な輪郭を抽出する方法、外接矩形として取得する方法など発展的な内容もあるため、継続して紹介していきたいと思います。
コメント