OpenCVのWatershedで画像の領域分割


 

目次

1. Watershed画像分割とは
2. OpenCVのWatershed
3. 実験
_3.1 データロード
_3.2 画像加工
_3.3 輪郭検(findContours)
_3.4 画像分割(watershed)

前回の記事は「OpenCV-Pythonでの図形検出、図形数える」を説明しました。Contoursのモジュールは、領域輪郭の図形検出ができますが、画像の領域分割はできません。画像分割の場合は、OpenCVのWatershedアルゴリズムを利用します。Watershedアルゴリズムとは、くっついているものを分離する事もできる古典的領域分割アルゴリズムです。日本語だと分水嶺アルゴリズムと言われています。画像の輝度勾配を山嶺とみなし、山の高い(= 輝度値の高い)位置から流れ込む水の作る領域をひとつの領域とする分割を行います。そのため輝度がはっきりと異なる場合は、弱いアルゴリズムです。

1. Watershed画像分割とは

Watershedは、得られた画像を意味のある領域に分割する事は重要な技術です。Watershedアルゴリズムは画像ピラミッドによる画像のセグメント化・平均値シフト法による画像のセグメント化します。

2. OpenCVのWatershed

cv.watershed(image, markers)

image : 入力画像

markers:  画像のマーカー(画像と同じサイズ)

https://docs.opencv.org/master/d7/d1b/group__imgproc__misc.html#ga3267243e4d3f95165d55a618c65ac6e1

3. 実験

環境:Google Colab
データセット:硬貨の画像
ライブラリ:OpenCV
分析:画像分割(watershed)

3.1 データロード

ライブラリインポート

import cv2

import numpy as np

import imutils

from IPython.display import Image

画像のファイルを表示します。

img_file = “coin02.png”

Image(img_file)

3.2 画像加工

pyrMeanShiftFilteringで同様の色分布で色を中和します。

image = cv2.imread(img_file)

shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)

 

output_img = “coin01_new.png”

cv2.imwrite(output_img, shifted)

Image(output_img)

THRESH_BINARYで白黒画像に変換します。

gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)

thresh = cv2.threshold(gray, 0, 255,

cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

 

output_img = “coin01_new.png”

cv2.imwrite(output_img, thresh)

Image(output_img)

3.3 輪郭検(findContours)

findContoursで輪郭検を行います。3つの画像輪郭を検出しましたが、領域分割はできません。

# 輪郭検contours

cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,

cv2.CHAIN_APPROX_SIMPLE)

cnts = imutils.grab_contours(cnts)

print(“[INFO] {} unique contours found”.format(len(cnts)))

 

# 輪郭検loop

for (i, c) in enumerate(cnts):

# draw the contour

((x, y), _) = cv2.minEnclosingCircle(c)

cv2.putText(image, “{}”.format(i + 1), (int(x) – 10, int(y)),

cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 0, 0), 3)

cv2.drawContours(image, [c], -1, (0, 255, 0), 2)

 

output_img = “coin01_new1.png”

cv2.imwrite(output_img, image)

Image(output_img)

3.4 画像分割(watershed)

Watershedで画像の領域分割を検出しました。6枚の硬貨を検出しました。

from skimage.feature import peak_local_max

from skimage.morphology import watershed

from scipy import ndimage

 

image = cv2.imread(img_file)

 

D = ndimage.distance_transform_edt(thresh)

localMax = peak_local_max(D, indices=False, min_distance=60,

labels=thresh)

 

# 画像分割(watershed)

markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]

labels = watershed(-D, markers, mask=thresh)

 

# loop

for label in np.unique(labels):

if label == 0:

continue

# マーク作成

mask = np.zeros(gray.shape, dtype=”uint8″)

mask[labels == label] = 255

 

# 輪郭検

cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,

cv2.CHAIN_APPROX_SIMPLE)

cnts = imutils.grab_contours(cnts)

c = max(cnts, key=cv2.contourArea)

 

# 輪郭検作成

((x, y), r) = cv2.minEnclosingCircle(c)

cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)

cv2.putText(image, “{}”.format(label), (int(x) – 10, int(y)),

cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 0, 0), 3)

 

output_img = “coin01_new2.png”

cv2.imwrite(output_img, image)

Image(output_img)

Watershedで別の画像の領域分割を検出しました。さまざまな種類のコインを検出できました。しかし重要な事にコインの位置を検出しただけであってコインの種類までは検出していません。また重なったものをそれぞれ別のものとして検出できています。これが重要です。

 

担当者:HM

香川県高松市出身 データ分析にて、博士(理学)を取得後、自動車メーカー会社にてデータ分析に関わる。その後コンサルティングファームでデータ分析プロジェクトを歴任後独立 気が付けばデータ分析プロジェクトだけで50以上担当

理化学研究所にて研究員を拝命中 応用数理学会所属