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.MorphologyEx: Erode/Dilate を組み合わせたオープニング・クロージング・勾配抽出などを一括で処理できる上位API
- Cv2.GetStructuringElement: 本記事で使用。構造要素の形状・サイズを定義する
- 二値化の前処理としてフィルタリングが必要な場合は以下も参考にしてください:
- Cv2.MedianBlur の使い方【OpenCV/C#(OpenCvSharp)】〜メディアンフィルタで画像のノイズを除去する〜
- Cv2.GaussianBlur の使い方【OpenCV/C#(OpenCvSharp)】〜ガウシアンぼかしで画像を平滑化する〜
- Cv2.Threshold の使い方【OpenCV/C#(OpenCvSharp)】〜画像を二値化する(大津の方法含む)〜
まとめ
Cv2.Erode(収縮)は白領域を縮小してノイズ除去に、Cv2.Dilate(膨張)は白領域を拡大して穴埋めや領域接続に使います。Cv2.GetStructuringElement でカーネルの形状とサイズを適切に設定し、iterations で強度を調整するのが実務の基本パターンです。カーネルを含む Mat は using で確実に解放することを忘れずに。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

