Pythonで記号の輪郭を検出する(OpenCV編)
PythonのOpenCV(cv2)モジュールを使って、画像中の記号の輪郭を検出してみます。
目次
輪郭を検出する方法
PythonのOpenCV(cv2)モジュールのfindContoursメソッドで、画像の輪郭を検出できます。ただし、輪郭を検出する前に準備が必要です。
カラー画像をグレースケールに変換
グレースケール画像を2値画像に変換
2値画像から輪郭を検出
findContoursの使い方は次の通りです。
contours, hierarchy = cv2.findContours(image, mode, method)
変数 |
型 |
内容 |
---|---|---|
image |
ndarray |
解析する画像のデータ。 |
mode |
輪郭情報の出力モード。 |
|
method |
輪郭の表現の仕方。 |
|
contours |
list |
検出した輪郭のリスト。 |
hierarchy |
ndarray |
検出した輪郭の階層情報。 |
modeに指定できる項目は下表です。輪郭の階層関係を使わないのであれば、RETR_EXTERNALとRETR_LISTが使い勝手良さそうですね。
RETR_EXTERNAL |
一番外側の輪郭を出力する。 |
RETR_LIST |
全ての輪郭を出力する。 |
RETR_CCOMP |
全ての輪郭を2レベルの階層に分けて出力する。 |
RETR_TREE |
全ての輪郭を全体の階層構造で出力する。 |
RETR_FLOODFILL |
methodに指定できる項目は下表です。CHAIN_APPROX_NONEよりもCHAIN_APPROX_SIMPLEの方が、消費するメモリ量が少なくなります。
CHAIN_APPROX_NONE |
輪郭の出力に、全ての検出点を含めます。 |
CHAIN_APPROX_SIMPLE |
輪郭を出力するときに、省略できる検出点は省略します。 |
CHAIN_APPROX_TC89_L1 |
|
CHAIN_APPROX_TC89_KCOS |
輪郭を表示する方法
findContoursをいろいろ試してみるのですが、その前に検出した輪郭を表示できるようにしましょう。
cv2.drawContours(image, contours, contourIdx, color, [thickness], [lineType])
変数 |
型 |
内容 |
---|---|---|
image |
ndarray |
輪郭を書き込む画像のデータ。 |
contours |
list |
輪郭のリスト。 |
contourIdx |
輪郭のインデックス。-1にすると全ての輪郭を表す。 |
|
color |
tuple |
輪郭線の色。 |
thickness |
int |
省略可。既定値は1。輪郭線の太さ。 |
lineType |
省略可。既定値はLINE_8。輪郭線の線種。 |
imageに輪郭が書き込まれますので注意してください。
輪郭検出の実施例
この画像の輪郭を検出してみます。

コードはこんな感じです。
import cv2
im = cv2.imread('test.png')
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
retval, im_bw = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 輪郭の検出
contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 全ての輪郭を書き込んで出力
im_con = im.copy()
cv2.drawContours(im_con, contours, -1, (0,255,0), 2)
cv2.imwrite('result.png', im_con)
こういう出力になります。

画像全体の外側に輪郭が出来てしまっていますね。これはちょっとよろしくありません。
背景色が黒で、前景色が白なんですね。
cv2.THRESH_BINARYをcv2.THRESH_BINARY_INVに書き換えて白黒反転させると、こういう出力になります。

輪郭を検出できました。
では、輪郭を1つずつ出力してみてみましょう。
import cv2
im = cv2.imread('test.png')
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
retval, im_bw = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 輪郭の検出
contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 輪郭を1つずつ書き込んで出力
for i in range(len(contours)):
im_con = im.copy()
cv2.drawContours(im_con, contours, i, (0,255,0), 2)
cv2.imwrite('result' + str(i) + '.png', im_con)










概ね期待通りですね。大きさなどでフィルターをするのが良さそうです。
では、一番外側の輪郭だけを取りだしてみましょう。cv2.RETR_LISTをcv2.RETR_EXTERNALに変更します。
import cv2
im = cv2.imread('test.png')
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
retval, im_bw = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 輪郭の検出
contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 全ての輪郭を書き込んで出力
im_con = im.copy()
cv2.drawContours(im_con, contours, -1, (0,255,0), 2)
cv2.imwrite('result.png', im_con)
# 輪郭を1つずつ書き込んで出力
for i in range(len(contours)):
im_con = im.copy()
cv2.drawContours(im_con, contours, i, (0,255,0), 2)
cv2.imwrite('result' + str(i) + '.png', im_con)
検出した輪郭を全て書き込んだものがこちらです。

輪郭を1つずつ取り出すとこうなります。


公開日
広告
Pythonで画像処理カテゴリの投稿
- Python (Pillow)でグラデーション画像を作る
- PythonでGIFアニメを作る(PILでファイルをつなげる編)
- PythonでOpenCVの画像データをMatplotlibで表示する
- PythonでPDFを画像に変換する(Windows10)
- PythonでPSDファイルをJPEGやPNGに変換する
- Pythonでカラー画像をグレイスケールに変換する(OpenCV編)
- Pythonでフォルダ内に同じ画像がないか検索してみた
- Pythonでマスク画像を作る方法(3選)
- Pythonで図形の外接矩形を描く(OpenCV編)
- Pythonで図形の輪郭の大きさを調べる(OpenCV編)
- Pythonで画像の余白を削除する(OpenCV編)
- Pythonで画像の余白を削除する(Pillow編)
- Pythonで画像の大きさやフォーマットを調べる(Pillow編)
- Pythonで画像の減色をする
- Pythonで画像をトリミングする(Pillow編)
- Pythonで画像を他の画像にペーストする(Pillow編)
- Pythonで画像を回転する(Pillow編)
- Pythonで画像を拡大縮小(リサイズ)する(Pillow編)
- Pythonで画像を読み込み、表示し、保存する(OpenCV編)
- Pythonで画像を読み込み、表示し、保存する(Pillow編)
- Pythonで画像を2値化する(OpenCV編)
- Pythonで記号の輪郭を検出する(OpenCV編)
- PythonとOpenCVで画像をグレースケールに変換してみた
- PythonとOpenCVで画像をネガポジ反転してみた