cv::erode / cv::dilate の使い方【OpenCV/C++】〜収縮・膨張処理の基本と実践〜
cv::erode は画像を収縮(エロージョン)、cv::dilate は膨張(ダイレーション)する関数です。二値画像のノイズ除去や輪郭の強調、文字認識前処理など、画像処理の実務で日常的に使います。最小限の呼び出しは次のとおりです。
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::erode(src, dst, kernel); // 収縮
cv::dilate(src, dst, kernel); // 膨張
動作環境
- OpenCV 4.x
- コンパイル例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// グレースケールで読み込み
cv::Mat src = cv::imread("sample.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return 1;
}
// 大津法で二値化
cv::Mat binary;
cv::threshold(src, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// 3×3 の矩形カーネルを作成
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 収縮(erode)
cv::Mat eroded;
cv::erode(binary, eroded, kernel);
// 膨張(dilate)
cv::Mat dilated;
cv::dilate(binary, dilated, kernel);
cv::imwrite("eroded.png", eroded);
cv::imwrite("dilated.png", dilated);
std::cout << "保存完了: eroded.png / dilated.png" << std::endl;
return 0;
}
実行結果:
保存完了: eroded.png / dilated.png
実行すると、収縮処理が適用された eroded.png(白い領域が一回り小さくなった画像)と、膨張処理が適用された dilated.png(白い領域が一回り大きくなった画像)が出力されます。
コード解説
| 行の処理 | ポイント |
|---|---|
cv::IMREAD_GRAYSCALE |
モルフォロジー処理はグレースケール/二値画像で使うことが多い |
cv::THRESH_OTSU |
手動で閾値を決めずに済む。照明が安定しない環境でも有効 |
cv::getStructuringElement |
カーネル(構造要素)を生成する専用関数。手書きの Mat より推奨 |
cv::erode |
カーネル内の最小値をピクセルに適用→白領域が縮む |
cv::dilate |
カーネル内の最大値をピクセルに適用→白領域が広がる |
引数と戻り値
cv::erode / cv::dilate 共通シグネチャ
void cv::erode(
InputArray src, // 入力画像
OutputArray dst, // 出力画像
InputArray kernel, // 構造要素(カーネル)
cv::Point anchor = cv::Point(-1, -1), // アンカー位置(-1,-1 で中心)
int iterations = 1, // 繰り返し回数
int borderType = cv::BORDER_CONSTANT, // 境界処理の種類
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue() // 境界値
);
cv::dilate もシグネチャは同じです。
| 引数 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力画像。チャンネル数は問わない(各チャンネルに独立して適用) |
dst |
OutputArray |
出力画像。src と同サイズ・同型 |
kernel |
InputArray |
構造要素。cv::getStructuringElement で生成するのが定石 |
anchor |
cv::Point |
カーネル内のアンカー位置。(-1, -1) で中心 |
iterations |
int |
処理の繰り返し回数。2 以上でより強い効果 |
borderType |
int |
境界ピクセルの扱い方 |
cv::getStructuringElement
cv::Mat cv::getStructuringElement(
int shape, // MORPH_RECT / MORPH_ELLIPSE / MORPH_CROSS
cv::Size ksize, // カーネルサイズ
cv::Point anchor = cv::Point(-1, -1)
);
shape の選択肢 |
用途 |
|---|---|
cv::MORPH_RECT |
矩形。最も一般的 |
cv::MORPH_ELLIPSE |
楕円形。丸みのある形状に適する |
cv::MORPH_CROSS |
十字形。縦横方向のみに作用させたいとき |
実践例
1. 塩胡椒ノイズの除去(オープニング処理)
収縮→膨張の順に適用する「オープニング」は、細かい白ノイズ(孤立した白点)の除去に有効です。cv::morphologyEx で一発で書けます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat src = cv::imread("noisy.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return 1;
}
cv::Mat binary;
cv::threshold(src, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// オープニング = erode → dilate
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::Mat opened;
cv::morphologyEx(binary, opened, cv::MORPH_OPEN, kernel);
cv::imwrite("opened.png", opened);
std::cout << "オープニング完了: opened.png" << std::endl;
return 0;
}
実行すると、opened.png に細かな白ノイズが除去された二値画像が出力されます。メディアンフィルタ(cv::medianBlur)と用途が近いですが、オープニングは二値化後の後処理に向いており、グレースケールのノイズ除去には medianBlur が適しています。
2. 文字の太さを変える(iterations の活用)
OCR 前処理で文字が細すぎる場合、iterations を増やして太らせると認識率が上がることがあります。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat src = cv::imread("text.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return 1;
}
cv::Mat binary;
// 文字が黒・背景が白の場合は THRESH_BINARY_INV を使う
cv::threshold(src, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 2回膨張して文字を太らせる
cv::Mat thick;
cv::dilate(binary, thick, kernel, cv::Point(-1, -1), 2);
cv::imwrite("thickened.png", thick);
std::cout << "太字化完了: thickened.png" << std::endl;
return 0;
}
実行すると、文字領域が2段階膨張した thickened.png が出力されます。iterations を変えるだけでカーネルを大きくするより細かくコントロールできます。
つまずきポイント
⚠️ カーネルサイズは奇数が基本
cv::getStructuringElement に偶数サイズを渡してもエラーにはなりません。しかしアンカー(中心点)がずれ、意図しない方向に処理が偏ります。3×3 / 5×5 / 7×7 など奇数サイズを使うのが原則です。
⚠️ 白・黒の定義に注意する
cv::erode は「白(前景)が縮む」処理です。二値化したとき、注目対象が黒で背景が白の画像では効果が逆になります。対象が黒い画像には cv::THRESH_BINARY_INV で反転して処理するか、cv::dilate と cv::erode を入れ替えてください。
⚠️ カラー画像に適用したときの挙動
cv::erode / cv::dilate はカラー画像(CV_8UC3)にも適用できますが、チャンネルが独立して処理されます。自然画像に使うと色が変色したように見える場合があります。モルフォロジー処理が目的ならグレースケール or 二値化後に適用するのが実務の定石です。
関連する関数
- cv::morphologyEx — オープニング・クロージング・トップハットなどの高次モルフォロジー処理。
erode/dilateを組み合わせる場面ではこちらを使うとシンプルに書ける - cv::getStructuringElement — カーネル生成の専用関数。本記事と必ずセットで使う
- cv::threshold の使い方【OpenCV/C++】〜二値化と大津の方法〜 — モルフォロジー処理の前段として必ず登場する
- cv::adaptiveThreshold の使い方【OpenCV/C++】〜適応的二値化で照明ムラに強い2値化を実現する〜 — 照明ムラがある環境での二値化後、erode/dilate でノイズを後処理するパターンが多い
- cv::medianBlur の使い方【OpenCV/C++】〜メディアンフィルタで塩胡椒ノイズを除去する〜 — 二値化前のグレースケールノイズ除去にはこちらが適している
まとめ
cv::erodeで白領域を縮小、cv::dilateで白領域を拡大する。カーネルはcv::getStructuringElementで生成するのが基本。iterationsを増やすことでカーネルを大きくせずに効果を段階的に強められる。- ノイズ除去や前処理で
erode → dilate(オープニング)の組み合わせが頻出。複合操作はcv::morphologyExを使うとコードがシンプルになる。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

