はじめに
OpenCVには画像の形状操作を実現する「モルフォロジー演算」の関数があります。
モルフォロジー演算により、ノイズ除去、小さな穴の消去などを実現できます。
今回はその中でもcv2.erode
関数とcv2.dilate
関数に焦点を当てて、解説していきます。
モルフォロジー演算とは
モルフォロジー演算は、画像の形状や構造を変化させる手法の総称です。
エッジの検出、ノイズ除去、輪郭の抽出などに利用されています。cv2.erode
関数とcv2.dilate
関数は代表的なモルフォロジー演算で、それぞれ「収縮」「膨張」の処理を行います。
モルフォロジー演算は、以下のような様々な場面で使用されています。
- ノイズ除去
cv2.erode
関数を使って、小さなノイズを除去できます。- Opening演算(erode → dilate)は、ノイズ除去に非常に有効です。
- 輪郭の抽出・強調
cv2.dilate
関数を使って、物体の輪郭を強調できます。- モルフォロジーグラディエント(dilate - erode)は、輪郭の抽出に適しています。
- 細線化・太線化
cv2.erode
関数は細線化、cv2.dilate
関数は太線化に使えます。- 細線化は、文字認識などの前処理に役立ちます。
- 欠損部分の埋め立て
cv2.dilate
関数で、物体の欠損部分を埋めることができます。- Closing演算(dilate → erode)は、穴埋め・平滑化に適しています。
- 前処理・後処理
- 他の画像処理アルゴリズムの前処理・後処理としてモルフォロジー演算を使うことがあります。
- エッジ検出の前処理や、二値化後の後処理など。
- 機械学習・深層学習
- データの前処理としてモルフォロジー演算を利用する場合があります。
- 画像認識や物体検出などの分野で利用する場合があります。
cv2.erode関数
cv2.erode
関数は、指定された構造化要素(カーネル)より小さい領域を削除することで、画像を収縮させる関数です。
エッジやノイズの除去、輪郭の細線化などに用いられます。
cv2.erode
関数の引数と戻り値は下の通りです。
引数
名称 | 説明 |
---|---|
入力画像(必須) | グレースケールまたはカラーの入力画像データ。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
は、borderType
がcv2.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
関数の引数と戻り値は下の通りです。
引数
名称 | 説明 |
---|---|
入力画像(必須) | グレースケールまたはカラーの入力画像データ。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
は、borderType
がcv2.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: erode
Erodes an image by using a specific structuring element.
OpenCV: dilate
Dilates an image by using a specific structuring element.
OpenCV: morphologyDefaultBorderValue
returns "magic" border value for erosion and dilation. It is automatically transformed to Scalar::all(-DBL_MAX) for dilation.
■(広告)OpenCVの参考書としてどうぞ!■