OpenCV – モルフォロジー演算 (膨張、収縮、オープニング、クロージング)

目次

概要

モルフォロジー演算について解説し、OpenCV でモルフォロジー演算を行う方法を紹介します。モルフォロジー演算は、二値画像からノイズを削除したり、輪郭を抽出するのに役立ちます。

モルフォロジー演算

以下の手順で行うフィルタリング処理をモルフォロジー演算 (morpology operation) といいます。

  1. 構成要素 (structuring element) またはカーネル (kernel) という入力画像の走査を行うための各要素が0または1で構成された2次元の行列を用意します。通常は 3×3、5×5 など小さい形状のものが使用されます。

カーネル

  1. カーネルの原点 (origin) またはアンカー (anchor) と呼ばれる成分 (図の赤マス) を入力画像の画素に合わせて左上から右下にかけてスライドさせ、出力画像の画素を計算していきます。

カーネル

カーネルの値が1の成分 (青のマス) は、走査対象の画素 (赤のマス) の近傍を表します。例えば、モルフォロジー演算の一種である膨張演算では、近傍の最大値を対応する出力画像の画素にします。

この処理をすべての画素で行うと以下のようになります。 膨張演算により、入力画像の白の線が太くなりました。

カーネル

カーネルの作成

2次元配列を自分で定義するか、cv2.getStructuringElement() を使用して作成します。

cv2.getStructuringElement

retval = cv2.getStructuringElement(shape, ksize[, anchor])
引数
名前 デフォルト値
shape MorphShapes
近傍とする形状
  • cv2.MORPH_RECT: 長方形
  • cv2.MORPH_CROSS: 十字
  • cv2.MORPH_ELLIPSE: 楕円
ksize tuple
カーネルの大きさ
anchor tuple (-1, -1)
アンカー成分。(-1, -1) の場合はカーネルの中心。
返り値
名前 説明
dst カーネル
In [1]:
import cv2
import numpy as np
from IPython.display import Image, display
from matplotlib import pyplot as plt


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))
In [2]:
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
ellipse_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))

print("cv2.MORPH_RECT", rect_kernel, sep="\n")
print("cv2.MORPH_CROSS", cross_kernel, sep="\n")
print("cv2.MORPH_ELLIPSE", ellipse_kernel, sep="\n")
cv2.MORPH_RECT
[[1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]]
cv2.MORPH_CROSS
[[0 0 1 0 0]
 [0 0 1 0 0]
 [1 1 1 1 1]
 [0 0 1 0 0]
 [0 0 1 0 0]]
cv2.MORPH_ELLIPSE
[[0 0 1 0 0]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [0 0 1 0 0]]

モルフォロジー演算の種類

膨張

膨張 (dilation) 演算とは、走査対象の画素を近傍で最も大きい画素値に置き換えるモルフォロジー演算です。

位置 $(x, y)$ の入力画像の画素を $\text{src}(x, y)$、対応する出力画像の画素を $\text{dst}(x, y)$、カーネルの画素を $k(x’, y’)$ とたとき、次の式で表せます。

$$ \text{dst}(x, y) = \max_{k(x’, y’) \ne 0} \text{src}(x + x’, y + y’)] $$

cv2.dilate

dst = cv2.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
引数
名前 デフォルト値
src ndarray
入力画像
kernel ndarray
カーネル
anchor tuple (-1, -1)
アンカー成分。(-1, -1) の場合はカーネルの中心。
iterations int 1
適用回数
borderType BorderTypes cv2.BORDER_CONSTANT
パディング方法。デフォルトはゼロパディング。
borderValue int 0
BorderTypes=cv2.BORDER_CONSTANT の場合の値
返り値
名前 説明
dst 出力画像

サンプルコード

sample1.jpg

In [3]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample1.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))

# 2値画像を収縮する。
dst = cv2.dilate(img, kernel)
imshow(dst)

膨張演算を行うと入力画像の線が太くなります。 2値化やエッジ抽出した結果、輪郭の線が途切れている場合に膨張演算を行うことで線を繋げたい場合に活用できます。

収縮

収縮 (erosion) 演算とは、走査対象の画素を近傍で最も小さい画素値に置き換えるモルフォロジー演算です。

位置 $(x, y)$ の入力画像の画素を $\text{src}(x, y)$、対応する出力画像の画素を $\text{dst}(x, y)$、カーネルの画素を $k(x’, y’)$ とたとき、次の式で表せます。

$$ \text{dst}(x, y) = \min_{k(x’, y’) \ne 0} \text{src}(x + x’, y + y’)] $$

cv2.erode

dst = cv2.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
引数
名前 デフォルト値
src ndarray
入力画像
kernel ndarray
カーネル
anchor tuple (-1, -1)
アンカー成分。(-1, -1) の場合はカーネルの中心。
iterations int 1
適用回数
borderType BorderTypes cv2.BORDER_CONSTANT
パディング方法。デフォルトはゼロパディング。
borderValue int 0
BorderTypes=cv2.BORDER_CONSTANT の場合の値
返り値
名前 説明
dst 出力画像

サンプルコード

sample2.jpg

In [4]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample2.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))

# 2値画像を収縮する。
dst = cv2.erode(img, kernel)
imshow(dst)

収縮演算を行うと入力画像の白い領域が小さくなります。2値化後にゴマ粒ノイズを削除したい場合に活用できます。

オープニング

オープニング (Opening) は収縮を $n$ 回行った後に膨張を $n$ 回行います。ノイズ削除に利用されます。

オープニング

sample3.jpg

In [5]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample3.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))

# 2値画像を収縮する。
dst = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=2)
imshow(dst)

クロージング

クロージング (Closing) は膨張を $n$ 回行った後に収縮を $n$ 回行います。穴を埋めるのに利用されます。

クロージング

sample4.jpg

In [6]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample4.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

# 2値画像を収縮する。
dst = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=2)
imshow(dst)

モルフォロジー勾配

モルフォロジー勾配 (morphology gradient) は $n$ 回膨張した結果から $n$ 回収縮した結果を減算する演算です。エッジ抽出に利用されます。

モルフォロジー勾配

sample5.jpg

In [7]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample5.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

# 2値画像を収縮する。
dst = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel, iterations=2)
imshow(dst)

ブラックハット

ブラックハット (black hat) は入力画像とクロージングの結果の差をとる演算です。

ブラックハット

sample6.jpg

In [8]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample6.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

# 2値画像を収縮する。
dst = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel, iterations=2)
imshow(dst)

トップハット

トップハット (top hat) は入力画像とオープニングの結果の差をとる演算です。

トップハット

sample7.jpg

In [9]:
# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample7.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを作成する。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

# 2値画像を収縮する。
dst = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel, iterations=2)
imshow(dst)

参照文献

コメント

目次