Cv2.Erode / Cv2.Dilate の使い方【OpenCV/C#(OpenCvSharp)】〜収縮・膨張処理〜

OpenCV for C#
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

Cv2.Erode / Cv2.Dilate の使い方【OpenCV/C#(OpenCvSharp)】〜収縮・膨張処理〜

Cv2.Erode(収縮)と Cv2.Dilate(膨張)は、二値画像やグレースケール画像に対して形態学的処理を施す関数です。ノイズ除去・穴埋め・物体の輪郭強調など、画像処理パイプラインの前処理として頻繁に登場します。最小限の呼び出しは次のとおりです。

Cv2.Erode(src, dst, kernel);   // 収縮(白領域を縮小)
Cv2.Dilate(src, dst, kernel);  // 膨張(白領域を拡大)

動作環境

  • OpenCV 4.x(OpenCvSharp4)
  • .NET 8 コンソールアプリ(トップレベルステートメント)
  • NuGet: OpenCvSharp4 + OpenCvSharp4.runtime.win(Windows実行時)

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

using OpenCvSharp;

// 二値画像を生成(白い矩形+ノイズ点)
using var src = new Mat(200, 200, MatType.CV_8UC1, Scalar.Black);
Cv2.Rectangle(src, new Rect(50, 50, 100, 100), Scalar.White, -1);
// ノイズとして小さな白点を追加
src.Set<byte>(30, 30, 255);
src.Set<byte>(170, 160, 255);

// 構造要素(カーネル)を作成:3x3 の矩形
using var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));

// 収縮(白い領域が縮小・ノイズ除去に有効)
using var eroded = new Mat();
Cv2.Erode(src, eroded, kernel);

// 膨張(白い領域が拡大・穴埋めや繋がりの補完に有効)
using var dilated = new Mat();
Cv2.Dilate(src, dilated, kernel);

// 結果を保存
Cv2.ImWrite("src.png",     src);
Cv2.ImWrite("eroded.png",  eroded);
Cv2.ImWrite("dilated.png", dilated);

Console.WriteLine("src.png / eroded.png / dilated.png を保存しました");

実行結果

src.png / eroded.png / dilated.png を保存しました
  • src.png: 白い矩形と2つのノイズ点を含む元画像
  • eroded.png: 矩形の白領域が外側から1ピクセル削られ、ノイズ点が消滅
  • dilated.png: 矩形の白領域が外側に1ピクセル膨らみ、ノイズ点も拡大

コードのポイント

行 説明
Cv2.GetStructuringElement 収縮・膨張の「形」と「大きさ」を定義するカーネルを作成
Cv2.Erode 各ピクセルをカーネル領域内の最小値で置き換える(白が縮む)
Cv2.Dilate 各ピクセルをカーネル領域内の最大値で置き換える(白が広がる)
using var kernel GetStructuringElement が返す Mat も IDisposable。using で解放必須

引数と戻り値

Cv2.Erode / Cv2.Dilate(共通シグネチャ)

Cv2.Erode(InputArray src, OutputArray dst, InputArray kernel,
          Point? anchor = null, int iterations = 1,
          BorderTypes borderType = BorderTypes.Reflect101,
          Scalar? borderValue = null)
引数 型 説明
src InputArray 入力画像(任意チャンネル・任意深度)
dst OutputArray 出力画像(src と同サイズ・同型)
kernel InputArray 構造要素(GetStructuringElement で生成するのが一般的)
anchor Point? カーネル内のアンカー位置。null で中心
iterations int 処理を繰り返す回数(デフォルト 1)
borderType BorderTypes 境界のパディング方法
borderValue Scalar? BorderTypes.Constant 使用時の境界値

戻り値はなし(dst に結果が書き込まれます)。

Cv2.GetStructuringElement

Mat Cv2.GetStructuringElement(MorphShapes shape, Size ksize, Point? anchor = null)
引数 型 説明
shape MorphShapes Rect(矩形)/ Cross(十字)/ Ellipse(楕円)
ksize Size カーネルのサイズ(奇数を推奨)
anchor Point? アンカー位置。null で中心

実践例

実践例1: 反復処理でノイズを強力に除去する

iterations を増やすことで、より強い収縮・膨張を適用できます。実務では二値化後のゴマ塩ノイズ除去でよく使います。

using OpenCvSharp;

// グレースケール画像を読み込んで二値化
using var gray = Cv2.ImRead("input.png", ImreadModes.Grayscale);
if (gray.Empty()) { Console.WriteLine("画像が読み込めません"); return; }

using var binary = new Mat();
// 二値化で白い物体領域を抽出(大津の方法)
Cv2.Threshold(gray, binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);

// 5x5 楕円カーネル × 2回収縮 → 小さなノイズを除去
using var kernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(5, 5));
using var cleaned = new Mat();
Cv2.Erode(binary, cleaned, kernel, iterations: 2);

Cv2.ImWrite("binary.png", binary);
Cv2.ImWrite("cleaned.png", cleaned);
Console.WriteLine("binary.png / cleaned.png を保存しました");

実践例2: 膨張→収縮(クロージング)で穴を埋める

膨張→収縮の組み合わせは クロージング と呼ばれ、物体内部の小さな穴(欠け)を埋めるのに有効です。Cv2.MorphologyEx を使うとより簡潔に書けますが、仕組みを理解するためにあえて個別に書きます。

using OpenCvSharp;

using var src = Cv2.ImRead("input_binary.png", ImreadModes.Grayscale);
if (src.Empty()) { Console.WriteLine("画像が読み込めません"); return; }

using var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(7, 7));

// クロージング: 膨張してから収縮
using var dilated = new Mat();
using var closed  = new Mat();
Cv2.Dilate(src,     dilated, kernel);  // 先に膨張
Cv2.Erode(dilated,  closed,  kernel);  // 次に収縮

Cv2.ImWrite("closed.png", closed);
Console.WriteLine("closed.png を保存しました");

✅ Cv2.MorphologyEx(src, dst, MorphTypes.Close, kernel) でも同じ結果が得られます。ステップを個別に確認したい場合は上記の書き方が分かりやすいです。


つまずきポイント

⚠️ カーネルの Mat を using で解放しないとメモリリークする

Cv2.GetStructuringElement はネイティブ側にメモリを確保した Mat を返します。小さいカーネルでも必ず using で解放してください。ループ内で何度も生成する実装は特に注意が必要です。

// NG: カーネルを解放していない
var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
Cv2.Erode(src, dst, kernel);
// → kernel が GC に回収されるまでネイティブメモリが残り続ける

// OK
using var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
Cv2.Erode(src, dst, kernel);

⚠️ iterations を増やすのとカーネルを大きくするのは等価ではない

iterations: 3 と Size(7,7) のカーネルは処理結果が異なります。iterations はカーネル形状を繰り返し適用するため、形が蓄積されます。一方でカーネルを大きくすると1回の適用範囲が広がります。目的に応じて使い分けてください。実務では カーネルサイズで影響範囲を直感的にコントロールし、iterations は繰り返しで効果を調整 するケースが多いです。

⚠️ カラー画像にも適用できるが挙動に注意

Cv2.Erode / Cv2.Dilate は CV_8UC3 などカラー画像にも適用可能です。ただしチャンネルごとに独立して最小値/最大値を取るため、色が変化します。形態学的処理はグレースケールまたは二値画像に適用するのが基本です。カラー画像に使う場合は意図した効果かどうか必ず確認してください。


関連する関数


まとめ

Cv2.Erode(収縮)は白領域を縮小してノイズ除去に、Cv2.Dilate(膨張)は白領域を拡大して穴埋めや領域接続に使います。Cv2.GetStructuringElement でカーネルの形状とサイズを適切に設定し、iterations で強度を調整するのが実務の基本パターンです。カーネルを含む Mat は using で確実に解放することを忘れずに。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました