【Python・OpenCV】モルフォロジー演算による画像操作の基本(cv2.erode, cv2.dilate)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】モルフォロジー演算による画像操作の基本(cv2.erode, cv2.dilate)

2024-05-02

はじめに

OpenCVには画像の形状操作を実現する「モルフォロジー演算」の関数があります。
モルフォロジー演算により、ノイズ除去、小さな穴の消去などを実現できます。
今回はその中でもcv2.erode関数とcv2.dilate関数に焦点を当てて、解説していきます。

(広告) OpenCV関連書籍をAmazonで探す

モルフォロジー演算とは

モルフォロジー演算は、画像の形状や構造を変化させる手法の総称です。
エッジの検出、ノイズ除去、輪郭の抽出などに利用されています。
cv2.erode関数とcv2.dilate関数は代表的なモルフォロジー演算で、それぞれ「収縮」「膨張」の処理を行います。

モルフォロジー演算は、以下のような様々な場面で使用されています。

  1. ノイズ除去
    • cv2.erode関数を使って、小さなノイズを除去できます。
    • Opening演算(erode → dilate)は、ノイズ除去に非常に有効です。
  2. 輪郭の抽出・強調
    • cv2.dilate関数を使って、物体の輪郭を強調できます。
    • モルフォロジーグラディエント(dilate - erode)は、輪郭の抽出に適しています。
  3. 細線化・太線化
    • cv2.erode関数は細線化、cv2.dilate関数は太線化に使えます。
    • 細線化は、文字認識などの前処理に役立ちます。
  4. 欠損部分の埋め立て
    • cv2.dilate関数で、物体の欠損部分を埋めることができます。
    • Closing演算(dilate → erode)は、穴埋め・平滑化に適しています。
  5. 前処理・後処理
    • 他の画像処理アルゴリズムの前処理・後処理としてモルフォロジー演算を使うことがあります。
    • エッジ検出の前処理や、二値化後の後処理など。
  6. 機械学習・深層学習
    • データの前処理としてモルフォロジー演算を利用する場合があります。
    • 画像認識や物体検出などの分野で利用する場合があります。

cv2.erode関数

cv2.erode関数は、指定された構造化要素(カーネル)より小さい領域を削除することで、画像を収縮させる関数です。
エッジやノイズの除去、輪郭の細線化などに用いられます。

cv2.erode関数の引数と戻り値は下の通りです。

cv2.erode(入力画像, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

引数

名称説明
入力画像(必須)グレースケールまたはカラーの入力画像データ。numpy.ndarrayオブジェクト。
kernel(必須)モルフォロジー演算に使用する構造化要素(カーネル)
dst(オプション)結果がこの変数に格納されます。指定しない場合は、入力画像と同じサイズ・型で新しい配列が作成されて結果が格納され、戻り値として取得できます。
anchor(オプション)モルフォロジー演算の基準点を表すtuple。(デフォルト:(-1, -1))
iterations(オプション)モルフォロジー演算を繰り返す回数を正数(int)で指定します。(デフォルト:1)
borderType(オプション)BorderTypesで指定される、境界ピクセルの指定(デフォルト:cv2.BORDER_CONSTANT)
borderValue(オプション境界値として使用する値を表すscalar値。(デフォルト:cv2.morphologyDefaultBorderValue()
  • kernelは、モルフォロジー演算に使用される構造化要素(カーネル)のnumpy配列です。一般的には、np.ones((ksize, ksize), np.uint8)のように定義します。ここでksizeはカーネルサイズです。
  • anchorは、モルフォロジー演算の基準点を表すタプルです。デフォルトでは画像の中心が基準点になります。
  • iterationsは、モルフォロジー演算を繰り返す回数を指定します。デフォルトは1回ですが、大きな値を指定するとより強い効果が得られます。
  • borderTypeは、画像の端の扱い方を指定するフラグです。デフォルトではcv2.BORDER_CONSTANTが使用されます。
  • borderValueは、borderTypecv2.BORDER_CONSTANTの場合に、境界値として使用する値を指定します。

モルフォロジー演算では、カーネルの形状とサイズを適切に設定することが重要です。また、画像の特性に合わせてiterationsの値を調整することで、より良い結果が得られる可能性があります。

戻り値

収縮処理を施した結果の画像データをnumpy.ndarrayオブジェクトで返します。

使い方

以下にサンプルコードを示します。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 入力画像をグレースケールで読み込む
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを定義する
kernel_3x3 = np.ones((3, 3), np.uint8)
kernel_5x5 = np.ones((5, 5), np.uint8)

# 収縮処理を適用する
erosion_3x3_1 = cv2.erode(image, kernel_3x3, iterations=1)
erosion_3x3_2 = cv2.erode(image, kernel_3x3, iterations=2)
erosion_5x5_1 = cv2.erode(image, kernel_5x5, iterations=1)
erosion_5x5_2 = cv2.erode(image, kernel_5x5, iterations=2)

# 結果を表示
plt.rcParams["figure.figsize"] = [13,4]                                 # ウィンドウサイズを設定
title = "cv2.erode: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定

plt.subplot(151)                                                        # 1行3列の1番目の領域にプロットを設定
plt.imshow(image, cmap='gray')                                          # 入力画像をグレースケールで表示
plt.title('Original Image')                                             # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(152)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(erosion_3x3_1, cmap='gray')                                  # cv2.erodeの結果
plt.title('erode 3x3, iterations=1')                                    # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(153)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(erosion_3x3_2, cmap='gray')                                  # cv2.erodeの結果
plt.title('erode 3x3, iterations=2')                                    # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(154)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(erosion_5x5_1, cmap='gray')                                  # cv2.erodeの結果
plt.title('erode 5x5, iterations=1')                                    # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(155)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(erosion_5x5_2, cmap='gray')                                  # cv2.erodeの結果
plt.title('erode 5x5, iterations=2')                                    # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.show()

このコードでは、入力画像に対して、3x3と5x5のkernelを適用します。

# カーネルを定義する
kernel_3x3 = np.ones((3, 3), np.uint8)
kernel_5x5 = np.ones((5, 5), np.uint8)


iterationsは1回と2回の2種類を演算します。

# 収縮処理を適用する
erosion_3x3_1 = cv2.erode(image, kernel_3x3, iterations=1)
erosion_3x3_2 = cv2.erode(image, kernel_3x3, iterations=2)
erosion_5x5_1 = cv2.erode(image, kernel_5x5, iterations=1)
erosion_5x5_2 = cv2.erode(image, kernel_5x5, iterations=2)

最後に、matplotlibを使って、結果を表示します。

実行すると、以下のような画像が表示されます。
カーネルサイズとiterationsが大きいほど、効果が強いことがわかります。

cv2.dilate関数

cv2.dilate関数はcv2.erode関数とは逆に、指定されたカーネルに基づいて画像を膨張させる関数です。
物体の輪郭を太くしたり、欠損部分を埋めたりするために利用されます。

cv2.dilate関数の引数と戻り値は下の通りです。

cv2.dilate(入力画像, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

引数

名称説明
入力画像(必須)グレースケールまたはカラーの入力画像データ。numpy.ndarrayオブジェクト。
kernel(必須)モルフォロジー演算に使用する構造化要素(カーネル)
dst(オプション)結果がこの変数に格納されます。指定しない場合は、入力画像と同じサイズ・型で新しい配列が作成されて結果が格納され、戻り値として取得できます。
anchor(オプション)モルフォロジー演算の基準点を表すtuple。(デフォルト:(-1, -1))
iterations(オプション)モルフォロジー演算を繰り返す回数を正数(int)で指定します。(デフォルト:1)
borderType(オプション)BorderTypesで指定される、境界ピクセルの指定(デフォルト:cv2.BORDER_CONSTANT)
borderValue(オプション境界値として使用する値を表すscalar値。(デフォルト:cv2.morphologyDefaultBorderValue()
  • cv2.dilate関数の引数は、cv2.erode関数と同じです
  • kernelは、モルフォロジー演算に使用される構造化要素(カーネル)のnumpy配列です。一般的には、np.ones((ksize, ksize), np.uint8)のように定義します。ここでksizeはカーネルサイズです。
  • anchorは、モルフォロジー演算の基準点を表すタプルです。デフォルトでは画像の中心が基準点になります。
  • iterationsは、モルフォロジー演算を繰り返す回数を指定します。デフォルトは1回ですが、大きな値を指定するとより強い効果が得られます。
  • borderTypeは、画像の端の扱い方を指定するフラグです。デフォルトではcv2.BORDER_CONSTANTが使用されます。
  • borderValueは、borderTypecv2.BORDER_CONSTANTの場合に、境界値として使用する値を指定します。

cv2.dilate関数はcv2.erode関数と逆の処理を行い、画像を膨張させます。
物体の輪郭を太くしたり、欠損部分を埋めたりするのに適しています。
モルフォロジー演算では、カーネルの形状とサイズを適切に設定することが重要です。また、iterationsの値を調整することで、より強い効果を得ることができます。

戻り値

膨張処理を施した結果の画像データをnumpy.ndarrayオブジェクトで返します。

使い方

以下にサンプルコードを示します。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 入力画像をグレースケールで読み込む
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを定義する
kernel_3x3 = np.ones((3, 3), np.uint8)  
kernel_5x5 = np.ones((5, 5), np.uint8)  

# 膨張処理を適用する
dilation_3x3_1 = cv2.dilate(image, kernel_3x3, iterations=1)
dilation_3x3_2 = cv2.dilate(image, kernel_3x3, iterations=2)
dilation_5x5_1 = cv2.dilate(image, kernel_5x5, iterations=1)
dilation_5x5_2 = cv2.dilate(image, kernel_5x5, iterations=2)

# 結果を表示
plt.rcParams["figure.figsize"] = [13,4]                                 # ウィンドウサイズを設定
title = "cv2.dilate: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定

plt.subplot(151)                                                        # 1行3列の1番目の領域にプロットを設定
plt.imshow(image, cmap='gray')                                          # 入力画像をグレースケールで表示
plt.title('Original Image')                                             # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(152)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(dilation_3x3_1, cmap='gray')                                 # cv2.dilateの結果
plt.title('dilate 3x3, iterations=1')                                   # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(153)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(dilation_3x3_2, cmap='gray')                                 # cv2.dilateの結果
plt.title('dilate 3x3, iterations=2')                                   # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(154)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(dilation_5x5_1, cmap='gray')                                 # cv2.dilateの結果
plt.title('dilate 5x5, iterations=1')                                   # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(155)                                                        # 1行3列の2番目の領域にプロットを設定
plt.imshow(dilation_5x5_2, cmap='gray')                                 # cv2.dilateの結果
plt.title('dilate 5x5, iterations=2')                                   # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.show()

このコードでは、入力画像に対して、3x3と5x5のkernelを適用します。

# カーネルを定義する
kernel_3x3 = np.ones((3, 3), np.uint8)
kernel_5x5 = np.ones((5, 5), np.uint8)


iterationsは1回と2回の2種類を演算します。

# 収縮処理を適用する
erosion_3x3_1 = cv2.dilate(image, kernel_3x3, iterations=1)
erosion_3x3_2 = cv2.dilate(image, kernel_3x3, iterations=2)
erosion_5x5_1 = cv2.dilate(image, kernel_5x5, iterations=1)
erosion_5x5_2 = cv2.dilate(image, kernel_5x5, iterations=2)

最後に、matplotlibを使って、結果を表示します。

実行すると、以下のような画像が表示されます。
一番左が入力画像です。
cv2.erode関数と同様にカーネルサイズとiterationsが大きいほど、効果が強いことがわかります。

複合的なモルフォロジー演算

cv2.erode()cv2.dilate()を組み合わせることで、さらに高度な処理が可能になります。
代表例としては、Opening(開き)とClosing(閉じ)があります。

  • Opening: 収縮 -> 膨張の順に実行。ノイズ除去や細かい突起物の削除に適しています。
  • Closing: 膨張 -> 収縮の順に実行。小さな穴の埋め立てや輪郭の平滑化に適しています。

これらを組み合わせることで、下の図の様な細かなノイズを除去することができます。

サンプルコードは下の様になります。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 入力画像をグレースケールで読み込む
image = cv2.imread("noize_image.jpg", cv2.IMREAD_GRAYSCALE)

# カーネルを定義する
kernel_3x3 = np.ones((3, 3), np.uint8)

# 収縮してから膨張
denoized = cv2.erode(image, kernel_3x3, iterations=1)
denoized = cv2.dilate(denoized, kernel_3x3, iterations=1)

# 結果を表示
plt.rcParams["figure.figsize"] = [5,4]                                  # ウィンドウサイズを設定
title = "cv2.erode, cv2.dilate: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定

plt.subplot(121)                                                        # 1行2列の1番目の領域にプロットを設定
plt.imshow(image, cmap='gray')                                          # 入力画像をグレースケールで表示
plt.title('Noized Image')                                               # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(122)                                                        # 1行2列の2番目の領域にプロットを設定
plt.imshow(denoized, cmap='gray')                                       # ノイズ除去の結果
plt.title('De-noized Image')                                            # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.show()

実行した結果は次の様になります。
左側がノイズ除去前、右側がノイズ除去後の画像になります。

ノイズの大きさは1ピクセルと小さなものであるため、カーネルサイズが3x3、iterations=1で十分綺麗にノイズ除去ができています。
実際にはさまざまな大きさのノイズが想定されるので、場面に応じてカーネルサイズやiterationsを調整することが重要です。

おわりに

モルフォロジー演算は、画像処理において使用頻度の高い操作といえます。cv2.erode関数とcv2.dilate関数の操作を理解することで、細線化・太線化やノイズ除去などのタスクに役立てることができます。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

参考リンク

■(広告)OpenCVの参考書としてどうぞ!■

-Python, プログラミング, 画像処理
-