cv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜

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

cv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜

cv::absdiff は、2つの画像(または画像とスカラー値)の要素ごとの絶対差分を計算する関数です。背景差分による変化検出や、2フレーム間の動き検出など、実務で頻繁に登場します。

最小の呼び出しはこれだけです。

cv::absdiff(src1, src2, dst);

動作環境

  • OpenCV 4.x
  • コンパイル例: g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

まず「同じサイズ・同じ型の2枚の画像」を用意し、差分を取る基本例です。

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

int main()
{
    // 比較元の2枚を読み込む
    cv::Mat src1 = cv::imread("frame1.png");
    cv::Mat src2 = cv::imread("frame2.png");

    if (src1.empty() || src2.empty()) {
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return -1;
    }

    // サイズ・型が一致していなければ終了
    if (src1.size() != src2.size() || src1.type() != src2.type()) {
        std::cerr << "サイズまたは型が一致しません" << std::endl;
        return -1;
    }

    cv::Mat diff;
    cv::absdiff(src1, src2, diff); // 絶対差分を計算

    cv::imwrite("diff.png", diff);
    std::cout << "差分画像を diff.png に保存しました" << std::endl;

    return 0;
}

実行結果

差分画像を diff.png に保存しました

diff.png には、2枚の画像で画素値が異なる領域が明るく(白っぽく)表示された差分画像が保存されます。2枚が完全に同一であればすべて黒(0)になります。

行ごとの解説

  • cv::imread で2枚を読み込み、どちらかが空なら即終了します。
  • サイズ・型の不一致チェックは実務では必須です(後述)。
  • cv::absdiff(src1, src2, diff) が本体。dst[i] = |src1[i] - src2[i]| を全画素に適用します。
  • 出力 diff は入力と同じ型(CV_8UC3 など)になります。

引数と戻り値

引数 説明
src1 InputArray 入力画像1(または定数スカラー)
src2 InputArray 入力画像2(または定数スカラー)。src1 と同サイズ・同型
dst OutputArray 出力画像。src1 と同サイズ・同型

戻り値: なし(void

⚠️ src1src2 の一方をスカラー(cv::Scalar)にすることもできますが、片方が Mat、もう片方が Scalar の場合のみ有効です。両方スカラーは不可。


実践例

例1: グレースケールで差分を取り、二値化して変化領域を抽出する

カラー差分をそのまま扱うより、グレースケールに変換してから差分を取る方が閾値処理しやすく実務でよく使われます。

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

int main()
{
    cv::Mat src1 = cv::imread("frame1.png");
    cv::Mat src2 = cv::imread("frame2.png");

    if (src1.empty() || src2.empty()) {
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return -1;
    }

    // グレースケールに変換してから差分を取る
    cv::Mat gray1, gray2;
    cv::cvtColor(src1, gray1, cv::COLOR_BGR2GRAY);
    cv::cvtColor(src2, gray2, cv::COLOR_BGR2GRAY);

    cv::Mat diff;
    cv::absdiff(gray1, gray2, diff); // CV_8UC1 同士の差分

    // 閾値処理で変化領域を二値化
    cv::Mat binary;
    cv::threshold(diff, binary, 30, 255, cv::THRESH_BINARY);

    cv::imwrite("diff_gray.png", diff);
    cv::imwrite("binary.png", binary);

    // 変化画素数を表示
    int changedPixels = cv::countNonZero(binary);
    std::cout << "変化画素数: " << changedPixels << std::endl;

    return 0;
}

実行結果

変化画素数: 12453

binary.png に白で変化領域が表示されます。閾値 30 はノイズ耐性と検出感度のバランスで調整してください。


例2: 静止背景との差分で動体を検出する

固定カメラで撮影した背景画像と現フレームを比較するシンプルな背景差分です。

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

int main()
{
    cv::Mat background = cv::imread("background.png");
    cv::Mat current    = cv::imread("current.png");

    if (background.empty() || current.empty()) {
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return -1;
    }

    cv::Mat bgGray, curGray;
    cv::cvtColor(background, bgGray, cv::COLOR_BGR2GRAY);
    cv::cvtColor(current,    curGray, cv::COLOR_BGR2GRAY);

    cv::Mat diff, binary;
    cv::absdiff(bgGray, curGray, diff);
    cv::threshold(diff, binary, 25, 255, cv::THRESH_BINARY);

    // 変化領域を元画像に重ねて可視化
    cv::Mat result = current.clone();
    result.setTo(cv::Scalar(0, 0, 255), binary); // 変化箇所を赤で塗る

    cv::imwrite("result.png", result);
    std::cout << "結果を result.png に保存しました" << std::endl;

    return 0;
}

実行結果

結果を result.png に保存しました

result.png では、背景から変化した領域(動体など)が赤くハイライトされた画像が確認できます。

✅ より堅牢な背景差分が必要な場合は cv::createBackgroundSubtractorMOG2 などの統計的手法を検討してください。cv::absdiff は「完全固定背景」前提のシンプルな用途に向いています。


つまずきポイント

⚠️ 型の不一致による assertion エラー

cv::absdiffsrc1src2 のサイズ・型が完全に一致している必要があります。CV_8UC3(カラー)と CV_8UC1(グレースケール)を混在させると実行時に assertion エラーが出ます。

cv::Exception: (-215:Assertion failed) src1.size == src2.size && src1.type() == src2.type()

cvtColorresize で前処理を揃えてから渡してください。


⚠️ cv::subtract との違いを混同する

cv::subtract(src1, src2, dst) は符号付き差分であり、src1 < src2 の画素は負値になります。CV_8U では飽和演算のため 0 にクリップされ、下側の差分が消えますcv::absdiff は絶対値を取るため両方向の差分を正しく表現できます。変化検出が目的なら cv::absdiff を使うのが原則です。


⚠️ 浮動小数点型での使用

CV_32FCV_64F 型の画像でも cv::absdiff は使えます。ただし差分値の範囲が [0.0, ...] になるため、cv::imwrite で保存しても正しく可視化できません。cv::normalizeconvertTo でスケールしてから保存してください。


関連する関数


まとめ

cv::absdiff は2枚の画像の絶対差分を計算する関数で、背景差分や変化検出の最初のステップとして実務でよく使います。グレースケール変換後に差分を取り、cv::threshold で二値化するパターンがもっとも一般的です。型・サイズの不一致と cv::subtract との挙動の違いだけ押さえておけばすぐに使えます。

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

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

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

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