cv::bilateralFilter の使い方【OpenCV/C++】〜エッジを保ちながらノイズを除去するバイラテラルフィルタ〜

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

cv::bilateralFilter の使い方【OpenCV/C++】〜エッジを保ちながらノイズを除去するバイラテラルフィルタ〜

cv::bilateralFilter は、エッジを保持しながらノイズを除去できるフィルタです。ガウシアンフィルタと異なり、輝度差が大きいピクセル(=エッジ付近)には平滑化をかけないため、輪郭がぼけません。最小限の呼び出しは次のとおりです。

cv::bilateralFilter(src, dst, 9, 75, 75);

動作環境

  • OpenCV 4.x
  • C++17
  • ビルド例: 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");
    if (src.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat dst;

    // バイラテラルフィルタを適用
    // d=9: フィルタ径, sigmaColor=75: 色空間の標準偏差, sigmaSpace=75: 座標空間の標準偏差
    cv::bilateralFilter(src, dst, 9, 75, 75);

    // 結果を保存
    cv::imwrite("output.png", dst);

    std::cout << "完了: output.png に保存しました。" << std::endl;
    return 0;
}

実行結果:

完了: output.png に保存しました。

実行すると、エッジが維持されたままノイズが低減された output.png が生成されます。ガウシアンブラーと比べて輪郭線がシャープに残っていることが確認できます。

各行の解説

説明
cv::imread("input.png") 画像を BGR 形式で読み込む
src.empty() チェック ファイルが存在しない・パスが間違っている場合に早期リターン
cv::bilateralFilter(src, dst, 9, 75, 75) 本体。後述の引数テーブル参照
cv::imwrite("output.png", dst) フィルタ後の画像を PNG で保存

引数と戻り値

void cv::bilateralFilter(
    InputArray  src,         // 入力画像(8ビット or 浮動小数点、1ch or 3ch)
    OutputArray dst,         // 出力画像(src と同サイズ・同型)
    int         d,           // フィルタ径(奇数推奨。負値を指定すると sigmaSpace から自動計算)
    double      sigmaColor,  // 色空間の標準偏差(大きいほど色差を許容して平滑化が強まる)
    double      sigmaSpace,  // 座標空間の標準偏差(大きいほど遠いピクセルの影響が増す)
    int         borderType   // ボーダー処理(省略時: BORDER_DEFAULT)
);
引数 説明
src InputArray 入力画像。CV_8UC1 / CV_8UC3 / 浮動小数点型に対応
dst OutputArray 出力画像。src と同サイズ・同型で自動確保される
d int フィルタリングに使用する近傍ピクセルの直径。-1sigmaSpace から自動計算
sigmaColor double 色空間の標準偏差。10 程度で弱い平滑化、150 前後で非常に強い平滑化
sigmaSpace double 座標空間の標準偏差。d が正の場合は主に d が径を決定する
borderType int 端部の補完方法。通常はデフォルト(cv::BORDER_DEFAULT)のまま

d と sigmaSpace の関係

d-1 にすると sigmaSpace の値から自動的にフィルタ径が決まります。大きな画像でリアルタイム処理する場合は d=5 程度に固定し sigmaSpace を調整する方が処理時間を制御しやすいです。


実践例

例1: グレースケール画像へのノイズ除去

カメラの読み取り画像を二値化する前処理としてバイラテラルフィルタを使うケースです。エッジがぼけると二値化精度が落ちるため、cv::GaussianBlur より有利な場合があります。

#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 filtered;
    // グレースケール(1ch)もそのまま渡せる
    cv::bilateralFilter(src, filtered, 9, 75, 75);

    cv::Mat binarized;
    // バイラテラルフィルタ後に大津の方法で二値化
    cv::threshold(filtered, binarized, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

    cv::imwrite("binarized.png", binarized);
    std::cout << "二値化完了: binarized.png" << std::endl;
    return 0;
}

例2: sigmaColor を変えて平滑化の強さを比較する

#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
    cv::Mat src = cv::imread("input.png");
    if (src.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    // sigmaColor を変えた3パターンを保存して比較
    for (int sigma : {25, 75, 150}) {
        cv::Mat dst;
        cv::bilateralFilter(src, dst, 9, static_cast<double>(sigma), 75);

        std::string filename = "bilateral_sigma" + std::to_string(sigma) + ".png";
        cv::imwrite(filename, dst);
        std::cout << "保存: " << filename << std::endl;
    }

    return 0;
}

実行結果:

保存: bilateral_sigma25.png
保存: bilateral_sigma75.png
保存: bilateral_sigma150.png

sigma=25 はほぼ変化なし、sigma=150 はほぼガウシアンと同等の強い平滑化になります。まず sigma=75 を基準に調整するのが実務上の定石です。


つまずきポイント

⚠️ src と dst に同じ Mat を渡してはいけない

cv::bilateralFilter はインプレース処理(src == dst)をサポートしていません。同じ変数を渡すとアサーションエラーが発生します。

// NG: src と dst が同じ
cv::bilateralFilter(img, img, 9, 75, 75);

// OK: 別の Mat を用意する
cv::Mat result;
cv::bilateralFilter(img, result, 9, 75, 75);

⚠️ 処理速度が遅い

バイラテラルフィルタは計算コストが高く、フィルタ径 d を大きくするほど急激に重くなります。1280×720 の画像で d=15 にすると、cv::GaussianBlurcv::medianBlur と比べて数倍〜数十倍の時間がかかることがあります(OpenCV 4.x 時点)。リアルタイム処理では d=5〜9 に抑えることを強く推奨します。

⚠️ 浮動小数点入力は正規化範囲に注意

CV_32FC1 / CV_32FC3 の画像を渡す場合、sigmaColor は画素値スケールに合わせて調整が必要です。0〜255 スケールの 8 ビット画像想定で sigmaColor=75 を設定していると、0.0〜1.0 スケールの浮動小数点画像ではフィルタがほぼ効きません(sigmaColor=0.1〜0.3 程度が適切)。


関連する関数


まとめ

cv::bilateralFilter は、エッジを保持しながらノイズを除去したい場面で第一候補になるフィルタです。dsigmaColorsigmaSpace の3パラメータを調整することで平滑化の強さを制御でき、二値化などの前処理に特に有効です。ただし処理コストが高いため、フィルタ径は用途に合わせて最小限に抑えましょう。

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

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

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