cv::morphologyEx の使い方【OpenCV/C++】〜オープニング・クロージングでノイズ除去〜
cv::morphologyEx は、膨張・収縮を組み合わせたモルフォロジー変換をまとめて実行する関数です。オープニング(小さなノイズ除去)・クロージング(小さな穴埋め)・勾配抽出など6種類の操作を1回の呼び出しで行えます。
最小の呼び出し例は次のとおりです。
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::morphologyEx(src, dst, cv::MORPH_OPEN, 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("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
// 構造要素(カーネル)を作成: 5x5 の矩形
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
// オープニング: 収縮 → 膨張(小さなノイズを除去)
cv::Mat dst_open;
cv::morphologyEx(src, dst_open, cv::MORPH_OPEN, kernel);
// クロージング: 膨張 → 収縮(小さな穴・隙間を埋める)
cv::Mat dst_close;
cv::morphologyEx(src, dst_close, cv::MORPH_CLOSE, kernel);
cv::imwrite("result_open.png", dst_open);
cv::imwrite("result_close.png", dst_close);
std::cout << "処理完了: result_open.png / result_close.png" << std::endl;
return 0;
}
実行結果
処理完了: result_open.png / result_close.png
カレントディレクトリに result_open.png(白いゴミ点が消えた画像)と result_close.png(白い領域の小さな穴が埋まった画像)が出力されます。
各行の解説
| 処理 | 内容 |
|---|---|
cv::imread(..., IMREAD_GRAYSCALE) |
グレースケールで読み込む。二値画像に対して使うことが多い |
cv::getStructuringElement |
カーネル形状を作成。MORPH_RECT・MORPH_ELLIPSE・MORPH_CROSS から選ぶ |
cv::morphologyEx(..., cv::MORPH_OPEN, ...) |
収縮→膨張。白い孤立点(塩ノイズ相当)を除去する |
cv::morphologyEx(..., cv::MORPH_CLOSE, ...) |
膨張→収縮。白い領域内の小さな黒い穴を埋める |
引数と戻り値
void cv::morphologyEx(
InputArray src, // 入力画像
OutputArray dst, // 出力画像(src と同サイズ・同型)
int op, // 変換の種類(下表参照)
InputArray kernel, // 構造要素(getStructuringElement で生成)
Point anchor = Point(-1,-1), // カーネルのアンカー点(-1,-1 で中心)
int iterations = 1, // 繰り返し回数
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue()
);
op の主な選択肢
| 定数 | 内容 |
|---|---|
cv::MORPH_ERODE |
収縮 |
cv::MORPH_DILATE |
膨張 |
cv::MORPH_OPEN |
オープニング(収縮→膨張) |
cv::MORPH_CLOSE |
クロージング(膨張→収縮) |
cv::MORPH_GRADIENT |
モルフォロジー勾配(膨張-収縮)。エッジ抽出に使う |
cv::MORPH_TOPHAT |
トップハット(元画像-オープニング)。明るい局所特徴抽出 |
cv::MORPH_BLACKHAT |
ブラックハット(クロージング-元画像)。暗い局所特徴抽出 |
iterations を増やすと、1回のカーネル適用を繰り返します。カーネルサイズを大きくするのと似た効果ですが、大きいカーネルより計算コストを抑えられる場合があります。
実践例
実践例①:二値化後のノイズ除去パイプライン
カメラ画像を二値化した後、オープニングで白いゴミを取り、クロージングで文字・図形の隙間を埋める定番パターンです。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat src = cv::imread("document.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);
// 楕円カーネル: 文字の丸みに合わせて MORPH_ELLIPSE を選択
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
// オープニングでノイズ除去
cv::Mat opened;
cv::morphologyEx(binary, opened, cv::MORPH_OPEN, kernel, cv::Point(-1, -1), 2);
// クロージングで文字内の穴埋め
cv::Mat result;
cv::morphologyEx(opened, result, cv::MORPH_CLOSE, kernel, cv::Point(-1, -1), 2);
cv::imwrite("result_clean.png", result);
std::cout << "クリーンアップ完了: result_clean.png" << std::endl;
return 0;
}
iterations = 2 を指定し、カーネルを2回適用することで効果を強めています。カーネルサイズを大きくするより、小さいカーネルを複数回適用した方が輪郭が崩れにくい傾向があります。
実践例②:モルフォロジー勾配によるエッジ抽出
MORPH_GRADIENT は「膨張画像-収縮画像」を計算し、物体の輪郭(エッジ)を取り出します。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat src = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::Mat gradient;
cv::morphologyEx(src, gradient, cv::MORPH_GRADIENT, kernel);
cv::imwrite("result_gradient.png", gradient);
std::cout << "エッジ抽出完了: result_gradient.png" << std::endl;
return 0;
}
物体の境界が白く浮かび上がった画像が出力されます。Canny エッジとは異なり、閾値チューニングなしで安定したエッジを得やすいのが特徴です。
つまずきポイント
⚠️ カラー画像に適用すると各チャンネルに独立して処理される
cv::morphologyEx はカラー画像(CV_8UC3)にも適用できますが、BGR 各チャンネルに独立して膨張・収縮が走ります。二値化マスクに対して適用するつもりが、うっかりカラー画像のまま渡してしまうケースに注意してください。意図した結果にならない場合は cv::cvtColor でグレースケールに変換してから処理しましょう。
⚠️ iterations はカーネル適用の繰り返し回数であり、op の繰り返しではない
MORPH_OPEN で iterations=2 とした場合、「(収縮×2)→(膨張×2)」が行われます。「オープニングを2回」ではない点に注意してください。より強力にノイズを取りたい場合はカーネルサイズを大きくするか、morphologyEx 自体を2回呼び出す方が意図通りになります。
⚠️ Windows で Debug/Release・アーキテクチャを取り違えるとリンクエラーになる
Visual Studio でリンクするライブラリが opencv_world4xx.lib(Release)と opencv_world4xxd.lib(Debug)で異なります。プロジェクト構成を切り替えた後にリンクエラーが出たら、プロパティ → リンカー → 入力のライブラリ名を確認してください。詳細はopencv_world の使い方とリンク方法【OpenCV/C++ Windows】を参照してください。
関連する関数
- cv::erode / cv::dilate —
morphologyExが内部で使う収縮・膨張の単体版。動作を個別に確認したいときに使う - cv::getStructuringElement — 構造要素を作成する必須セットの関数
- cv::threshold の使い方【OpenCV/C++】〜二値化と大津の方法〜 — モルフォロジー変換の前処理として二値化がセットになることが多い
- cv::adaptiveThreshold の使い方【OpenCV/C++】〜適応的二値化で照明ムラに強い2値化を実現する〜 — 照明ムラがある場合は適応的二値化とモルフォロジーの組み合わせが定番
- cv::medianBlur の使い方【OpenCV/C++】〜メディアンフィルタで塩胡椒ノイズを除去する〜 — 塩胡椒ノイズ除去にはオープニングとメディアンフィルタどちらが適切かを比較してみると有益
まとめ
cv::morphologyEx は、オープニング・クロージング・勾配など6種類のモルフォロジー変換を1関数で呼び出せる実装上の要所です。二値化後のノイズ除去・穴埋めパイプラインにそのまま組み込めるため、実務でも使用頻度が高い関数です。カーネル形状(MORPH_RECT / MORPH_ELLIPSE)と iterations の調整で、後段の処理精度が大きく変わる点を押さえておきましょう。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

