次のチュートリアル: 輪郭の特徴
目的
輪郭とは何か?
輪郭は、同じ色または強度を持つ(境界に沿った)連続した点をすべて結んだ曲線として簡単に説明できる。輪郭は形状解析や物体検出・認識に役立つツールである。
- より高い精度を得るには、二値画像を使用する。そのため、輪郭を見つける前にしきい値処理やCannyエッジ検出を適用する。
- OpenCV 3.2以降、findContours()はソース画像を変更しなくなった。
- OpenCVでは、輪郭を見つけることは黒い背景から白い物体を見つけることに似ている。したがって、見つけたい物体は白く、背景は黒であるべきだという点を覚えておくこと。
二値画像の輪郭を見つける方法を見てみよう:
import numpy as np
import cv2 as cv
assert im is not None, "file could not be read, check with os.path.exists()"
contours, hierarchy =
cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
Converts an image from one color space to another.
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
Applies a fixed-level threshold to each array element.
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
Finds contours in a binary image.
見てのとおり、cv.findContours() 関数には3つの引数がある。1つ目はソース画像、2つ目は輪郭の検索モード、3つ目は輪郭の近似手法である。そして輪郭と階層構造を出力する。輪郭は画像中の全輪郭からなるPythonのリストである。各輪郭は、物体の境界点の(x,y)座標からなるNumpy配列である。
- 覚え書き
- 2つ目と3つ目の引数、および階層構造については後で詳しく説明する。それまでは、コードサンプルで与えた値ですべての画像で問題なく動作する。
輪郭をどのように描画するか?
輪郭を描画するには、cv.drawContours 関数を使用する。境界点さえあれば、任意の形状の描画にも使える。第1引数はソース画像、第2引数はPythonのリストとして渡すべき輪郭、第3引数は輪郭のインデックス(個々の輪郭を描画するときに有用。全輪郭を描画するには-1を渡す)、残りの引数は色や太さなどである。
- 画像中の全輪郭を描画するには:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar &color, int thickness=1, int lineType=LINE_8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point())
Draws contours outlines or filled contours.
- 個々の輪郭、たとえば4番目の輪郭を描画するには:
- しかし、ほとんどの場合は以下の方法が有用である:
- 覚え書き
- 最後の2つの方法は同じだが、先に進むにつれて最後のものがより有用であることがわかる。
輪郭近似手法
これは cv.findContours 関数の第3引数である。これは実際に何を表しているのか?
先ほど、輪郭は同じ強度を持つ形状の境界であると述べた。輪郭は形状の境界の(x,y)座標を格納する。しかし、すべての座標を格納するのだろうか?それを指定するのがこの輪郭の近似手法である。
cv.CHAIN_APPROX_NONE を渡すと、すべての境界点が格納される。しかし、実際にすべての点が必要だろうか?たとえば、直線の輪郭を見つけたとする。その直線を表現するのに、線上のすべての点が必要だろうか?いや、必要なのはその直線の2つの端点だけである。これが cv.CHAIN_APPROX_SIMPLE が行うことである。冗長な点をすべて取り除いて輪郭を圧縮し、メモリを節約する。
以下の長方形の画像がこの手法を示している。輪郭配列内のすべての座標に円を描いている(青色で描画)。1枚目の画像は cv.CHAIN_APPROX_NONE で得られた点(734点)を示し、2枚目の画像は cv.CHAIN_APPROX_SIMPLE で得られたもの(わずか4点)を示している。これでどれだけメモリを節約できるか見てみよ!!!
image