【C++】OpenCVを使った画像フィルタリングの基本手法と実例
C++とOpenCVを使った画像フィルタリングは、ノイズ除去やエッジ強調など実践的な処理が可能であり、ぼかし、ガウシアン、メディアン、バイラテラルなど多彩な手法で調整できます。
各フィルタは入力画像に対して最も適した処理を選択できるため、用途に応じた柔軟な画像加工が実現できるです。
画像フィルタリングの基本的な考え方
フィルタリングの目的と役割
画像フィルタリングは、画像中の不要なノイズを取り除いたりエッジや細かい情報を強調するための処理です。
たとえば、撮影条件やセンサーの影響で発生するノイズを軽減したり、画像の輪郭を抽出することで対象物の認識を容易にする効果があります。
フィルタリングを適用する際は、各フィルタの特性に応じたパラメータ設定が結果の品質に大きな影響を与えます。
OpenCVにおける画像処理の特徴
OpenCVは、豊富な画像処理関数を持つライブラリで、リアルタイム処理やマルチプラットフォーム対応が優れている点が魅力です。
多彩なフィルタやエッジ検出アルゴリズムが実装されており、シンプルなコードで複雑な処理を実現できるため、画像フィルタリングにおける実用性が高まっています。
また、パラメータ調整が容易なため、各種手法の試行錯誤を柔軟に行えるメリットがあります。
フィルタの種類と動作原理
ぼかし系フィルタ
ボックスフィルタ (Blur)
ボックスフィルタは、画像の各画素を周囲の画素値の単純な平均で置き換える処理です。
カーネル内のすべての画素に等しい重みを付けるため、計算が比較的軽く、画像全体のノイズを一様に除去する効果があります。
下記のサンプルコードは、cv::blur
関数を用いて「image.jpg」を読み込み、カーネルサイズ(5, 5)
で平滑化を行い、結果を「box_blur.jpg」として保存する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 画像ファイルをカラーで読み込み
Mat src = imread("image.jpg", IMREAD_COLOR);
if (src.empty()) {
cout << "画像が読み込めませんでした" << endl;
return -1;
}
Mat dst;
Size ksize(5, 5); // カーネルサイズの設定
blur(src, dst, ksize);
imwrite("box_blur.jpg", dst); // 処理結果を保存
cout << "ボックスフィルタの適用が完了しました" << endl;
return 0;
}
ボックスフィルタの適用が完了しました

ガウシアンフィルタ (Gaussian Blur)
ガウシアンフィルタは、周囲の画素に正規分布に従った重みを与え、滑らかにすることでノイズを効果的に除去します。
カーネルサイズと共に、標準偏差(σ)が結果に大きな影響を与えるため、対象画像に合わせたパラメータ設定が求められます。
下記のサンプルコードは、cv::GaussianBlur
関数でガウシアンフィルタを適用し、画像の平滑化を実現する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// カラー画像の読み込み
Mat src = imread("image.jpg", IMREAD_COLOR);
if(src.empty()) {
cout << "画像の読み込みに失敗しました" << endl;
return -1;
}
Mat dst;
Size ksize(5, 5); // カーネルサイズの設定
double sigmaX = 1.0; // X方向の標準偏差
GaussianBlur(src, dst, ksize, sigmaX);
imwrite("gaussian_blur.jpg", dst); // 処理結果を保存
cout << "ガウシアンフィルタの適用が完了しました" << endl;
return 0;
}
ガウシアンフィルタの適用が完了しました
メディアンフィルタ (Median Blur)
メディアンフィルタは、各画素を周囲の画素の中央値に置き換えることでノイズを低減します。
特に、塩と胡椒ノイズの除去に有効とされており、エッジ部分の保持にも優れている点が特徴です。
下記のサンプルコードは、cv::medianBlur
関数を利用し、画像に対してメディアンフィルタを実装する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// カラー画像の読み込み
Mat src = imread("image.jpg", IMREAD_COLOR);
if (src.empty()) {
cout << "画像が正常に読み込めませんでした" << endl;
return -1;
}
Mat dst;
int ksize = 5; // カーネルサイズ(奇数値)
medianBlur(src, dst, ksize);
imwrite("median_blur.jpg", dst); // 結果画像の保存
cout << "メディアンフィルタの適用が完了しました" << endl;
return 0;
}
メディアンフィルタの適用が完了しました
バイラテラルフィルタ (Bilateral Filter)
バイラテラルフィルタは、エッジ部分を保持しながら画像全体のノイズを除去する効果を持ちます。
空間的な近さと色の類似性に基づく重み付けを行うため、エッジのぼやけを防ぐ特徴があります。
以下のサンプルコードは、cv::bilateralFilter
を利用してバイラテラルフィルタを適用し、エッジを保ったノイズ除去を行う例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// カラー画像の読み込み
Mat src = imread("image.jpg", IMREAD_COLOR);
if(src.empty()) {
cout << "画像の読み込みに失敗しました" << endl;
return -1;
}
Mat dst;
int d = 9; // 各ピクセル近傍領域の直径の設定
double sigmaColor = 75.0; // 色空間におけるフィルタシグマ
double sigmaSpace = 75.0; // 座標空間におけるフィルタシグマ
bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);
imwrite("bilateral_filter.jpg", dst); // 処理結果を保持
cout << "バイラテラルフィルタの処理が完了しました" << endl;
return 0;
}
バイラテラルフィルタの処理が完了しました
エッジ検出系フィルタ
ソーベルフィルタ (Sobel)
ソーベルフィルタは、画像の輝度勾配を抽出するための手法です。
X方向およびY方向それぞれの勾配を計算し、最終的に合成することでエッジの存在を強調できるため、輪郭検出に適しています。
以下のサンプルコードは、cv::Sobel
を用いてグレースケール画像からX方向とY方向の勾配を算出し、これらを合成してエッジ画像を出力する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// グレースケール画像を読み込み
Mat src = imread("image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
cout << "画像が読み込めませんでした" << endl;
return -1;
}
Mat gradX, gradY, absGradX, absGradY, grad;
int ddepth = CV_16S; // 出力画像の深度
int ksize = 3; // カーネルサイズの設定
// X方向の勾配を計算
Sobel(src, gradX, ddepth, 1, 0, ksize);
// Y方向の勾配を計算
Sobel(src, gradY, ddepth, 0, 1, ksize);
// 絶対値を取得
convertScaleAbs(gradX, absGradX);
convertScaleAbs(gradY, absGradY);
// 両方向の勾配を加重合成
addWeighted(absGradX, 0.5, absGradY, 0.5, 0, grad);
imwrite("sobel_edge.jpg", grad); // 処理結果を保存
cout << "ソーベルフィルタによるエッジ検出が完了しました" << endl;
return 0;
}
ソーベルフィルタによるエッジ検出が完了しました

ラプラシアンフィルタ (Laplacian)
ラプラシアンフィルタは、画像の二階微分を計算してエッジを検出する方法です。
画像中の急激な輝度変化部分に対して高い反応を示すため、エッジや細部の情報強調に利用されます。
以下のサンプルコードは、cv::Laplacian
を使ってラプラシアン変換を実行し、エッジ情報を得る例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// グレースケール画像の読み込み
Mat src = imread("image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
cout << "画像が読み込めませんでした" << endl;
return -1;
}
Mat dst;
int ddepth = CV_16S; // 出力画像の深度設定
int ksize = 3; // カーネルサイズの設定
// ラプラシアンフィルタの適用
Laplacian(src, dst, ddepth, ksize);
Mat absDst;
convertScaleAbs(dst, absDst);
imwrite("laplacian_edge.jpg", absDst); // 結果画像の保存
cout << "ラプラシアンフィルタによるエッジ検出が完了しました" << endl;
return 0;
}
ラプラシアンフィルタによるエッジ検出が完了しました

モルフォロジー変換による処理
膨張・収縮処理
膨張処理(dilate)と収縮処理(erode)は、画像の構造要素の拡大・縮小を行う処理です。
膨張処理は画像中の明るい領域を広げ、収縮処理は暗い領域を強調することで、ノイズ除去や細かい部分の補正に役立ちます。
以下のサンプルコードは、cv::dilate
とcv::erode
を利用して画像に対して膨張処理と収縮処理を実装する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// グレースケール画像の読み込み
Mat src = imread("image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
cout << "画像の読み込みに失敗しました" << endl;
return -1;
}
Mat dilated, eroded;
// 構造要素を作成(矩形カーネル)
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
// 膨張処理を実施
dilate(src, dilated, element);
// 収縮処理を実施
erode(src, eroded, element);
imwrite("dilated.jpg", dilated); // 膨張処理結果の保存
imwrite("eroded.jpg", eroded); // 収縮処理結果の保存
cout << "膨張・収縮処理が完了しました" << endl;
return 0;
}
膨張・収縮処理が完了しました


モルフォロジー勾配
モルフォロジー勾配は、膨張処理と収縮処理の差分を計算することで、画像の輪郭情報を抽出する手法です。
細かいエッジ情報が強調されるため、対象物の形状認識に役立ちます。
下記のサンプルコードは、cv::morphologyEx
関数を利用してモルフォロジー勾配を計算し、画像中のエッジを抽出する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// グレースケール画像の読み込み
Mat src = imread("image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
cout << "画像が読み込めませんでした" << endl;
return -1;
}
Mat gradient;
// 構造要素の生成(矩形カーネル)
Mat element = getStructuringElement(MORPH_RECT, Size(5,5));
// モルフォロジー勾配の計算
morphologyEx(src, gradient, MORPH_GRADIENT, element);
imwrite("morph_gradient.jpg", gradient); // 結果画像の保存
cout << "モルフォロジー勾配の計算が完了しました" << endl;
return 0;
}
モルフォロジー勾配の計算が完了しました

パラメータ設計とフィルタ選択
カーネルサイズの影響
カーネルサイズはフィルタ処理の強さや対象となる領域を決定する重要なパラメータです。
大きなカーネルサイズを用いると、画像全体がより滑らかになり細かいノイズが除去される反面、エッジ情報もぼやけやすくなります。
一方、細かいカーネルではディテールが保存されるが、ノイズ除去効果が少なくなることがあるため、用途に合わせた適切な選択が必要です。
標準偏差や色空間の設定
パラメータ調整による結果の変動
ガウシアンフィルタの場合、標準偏差(
標準偏差が大きいと計算されるガウス分布が広がり、より広範囲にわたる平滑化が行われます。
バイラテラルフィルタも同様に、色空間や空間でのシグマ値が結果に影響し、適切な調整によってエッジ保持とノイズ除去のバランスを取ることが可能です。
設定例の考察
以下は、フィルタごとのパラメータ例の一部を表にまとめたものです。
- ガウシアンフィルタ
- カーネルサイズ:
: 1.0〜2.0
- カーネルサイズ:
- メディアンフィルタ
- カーネルサイズ: 3〜7(奇数値)
- バイラテラルフィルタ
- カーネル直径: 5〜9
- sigmaColor, sigmaSpace: 50.0〜100.0
これらの設定は画像の性質や目的に合わせて微調整する必要があり、実験的な検証を通して最適なパラメータが選ばれるケースが多くあります。
画像フィルタリングの応用事例
ノイズ除去の実例
撮影された画像に含まれるランダムなノイズを低減する際、メディアンフィルタやバイラテラルフィルタが効果的です。
特に塩と胡椒ノイズにおいては、メディアンフィルタがエッジを保ちながら不規則なノイズを除去するため、実用的な手法として活用されます。
場合によっては、前処理としてボックスフィルタで大まかなノイズを除去し、続いてメディアンフィルタで局所的な補正を行うといった複合処理も有用です。
エッジ抽出の実例
エッジ検出の目的の場合、ソーベルフィルタやラプラシアンフィルタが用いられます。
画像中の急激な輝度変化を抽出することで、対象物の輪郭や形状情報が強調され、物体認識や画像解析の前処理として役立ちます。
また、モルフォロジー勾配によって連続したエッジラインを抽出し、細かい特徴を明らかにするアプローチも実践例として存在します。
画像強調と変換処理
画像の明暗やコントラストを改善する際、エッジ情報を利用してシャープ化処理を行う方法があります。
たとえば、ラプラシアンフィルタで抽出したエッジ情報を元の画像に加算することで、ディテールが際立ち画像全体の印象がクリアになる手法などがあります。
これらの画像強調技術は、医療画像や監視カメラ映像など、重要なディテールが求められる用途で応用されています。
アルゴリズム比較と処理効率
各フィルタの特性比較
各種フィルタは、目的に応じた特性を持ちます。
主要なフィルタの特性を以下のリストにまとめました。
- ボックスフィルタ
- 計算が高速でシンプルな平滑化処理
- エッジ情報の保持が難しい
- ガウシアンフィルタ
- ノイズ除去と平滑化のバランスが取りやすい
- 標準偏差の設定次第で処理効果が大きく変化
- メディアンフィルタ
- 塩と胡椒ノイズに対して効果的
- 処理速度が比較的低下するケースもある
- バイラテラルフィルタ
- エッジ保持をしながらノイズを除去
- 計算コストが高い傾向にある
処理速度と精度のバランス
複雑な画像処理においては、処理速度と結果の精度のバランスを考えながらフィルタ選択を行う必要があります。
リアルタイム処理が求められる場合、処理時間が短いボックスやソーベルフィルタが選ばれることがあります。
一方で、精度が重視される場合は、パラメータ調整が可能なガウシアンフィルタやバイラテラルフィルタが好まれます。
並列処理活用の可能性
OpenCVは、マルチスレッドやSIMD命令による処理の高速化が可能なため、並列処理を活用することで処理効率の向上が期待できます。
特に大きな画像やリアルタイム映像処理において、ハードウェアの持つ並列性能を活かすことは、実用上のメリットが大きく、多くのプロジェクトで取り入れられています。
まとめ
今回の記事では、画像フィルタリングの基本的な考え方から具体的な各種フィルタの動作原理や実装例、さらにはパラメータの調整方法や各フィルタ間の特性比較について解説しました。
目的に応じたフィルタ選択やパラメータ設定が、画像処理の結果に大きな影響をもたらすため、プロジェクトごとの試行錯誤が大切です。
各サンプルコードを参考にしながら、適切な画像フィルタリング手法を導入していただけると幸いです。