Cv2.Absdiff の使い方【OpenCV/C#(OpenCvSharp)】〜差分画像を作る〜

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

Cv2.Absdiff の使い方【OpenCV/C#(OpenCvSharp)】〜差分画像を作る〜

Cv2.Absdiff は2枚の画像のピクセル単位で絶対差分(|src1 - src2|)を計算する関数です。背景差分による動体検出や、製品外観検査での差異検出など、実務でよく登場します。基本的な呼び出しは次のとおりです。

Cv2.Absdiff(src1, src2, dst);

動作環境

  • OpenCvSharp4(OpenCV 4.x 時点)
  • NuGet: OpenCvSharp4 + OpenCvSharp4.runtime.win(Windows実行時)
  • .NET 8 コンソールアプリ(トップレベルステートメント)

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

基本の使い方

2枚のグレースケール画像を読み込み、差分画像を生成して保存するサンプルです。

using OpenCvSharp;

// 比較する2枚の画像をグレースケールで読み込む
using var src1 = Cv2.ImRead("frame1.png", ImreadModes.Grayscale);
using var src2 = Cv2.ImRead("frame2.png", ImreadModes.Grayscale);

if (src1.Empty() || src2.Empty())
{
    Console.WriteLine("画像の読み込みに失敗しました。");
    return;
}

// 絶対差分を計算する
using var diff = new Mat();
Cv2.Absdiff(src1, src2, diff);

// 差分画像を保存する
Cv2.ImWrite("diff.png", diff);
Console.WriteLine("差分画像を diff.png に保存しました。");

実行すると、2枚の画像の差分が diff.png として保存されます。変化のないピクセルは黒(0)、差異があるピクセルは明るい値になります。

行ごとの解説

行 内容
ImRead(..., Grayscale) グレースケールで読み込み。チャンネル数をそろえる
Cv2.Absdiff(src1, src2, diff) ピクセルごとに |src1 - src2| を計算
ImWrite("diff.png", diff) 差分画像をファイルに書き出す

引数と戻り値

Cv2.Absdiff(InputArray src1, InputArray src2, OutputArray dst)
引数 型 説明
src1 InputArray 1枚目の入力画像(Mat、Scalar も可)
src2 InputArray 2枚目の入力画像(src1 と同サイズ・同型)
dst OutputArray 出力先の差分画像(src1 と同サイズ・同型)

戻り値はありません(void)。src1 と src2 のサイズ・チャンネル数・深度は一致している必要があります。カラー画像(BGR/3ch)にも適用できます。

実践例

背景差分で動体領域をマスク抽出する

固定カメラの「背景フレーム」と「現在フレーム」を比較し、動体をマスクとして取り出す典型パターンです。

using OpenCvSharp;

// 背景画像と現在フレームをグレースケールで読み込む
using var background = Cv2.ImRead("background.png", ImreadModes.Grayscale);
using var current    = Cv2.ImRead("current.png",    ImreadModes.Grayscale);

if (background.Empty() || current.Empty())
{
    Console.WriteLine("画像の読み込みに失敗しました。");
    return;
}

// 絶対差分を計算する
using var diff = new Mat();
Cv2.Absdiff(background, current, diff);

// 二値化してマスク画像を作成する(差分が小さいノイズを除去)
using var mask = new Mat();
Cv2.Threshold(diff, mask, thresh: 30, maxval: 255, type: ThresholdTypes.Binary);

Cv2.ImWrite("mask.png", mask);
Console.WriteLine("マスク画像を mask.png に保存しました。");

Cv2.Threshold で閾値30以下の微小差分(照明ノイズなど)を除去しています。この後、Cv2.ConnectedComponentsWithStats でラベリングすれば、動体領域ごとの面積・バウンディングボックスを取得できます。

カラー画像の差分を可視化する

using OpenCvSharp;

// カラー画像として読み込む(デフォルトはBGR)
using var img1 = Cv2.ImRead("product_ref.png");
using var img2 = Cv2.ImRead("product_test.png");

if (img1.Empty() || img2.Empty())
{
    Console.WriteLine("画像の読み込みに失敗しました。");
    return;
}

// カラーのまま差分を計算する
using var diff = new Mat();
Cv2.Absdiff(img1, img2, diff);

// 差分画像をグレースケールに変換して視認性を上げる
using var diffGray = new Mat();
Cv2.CvtColor(diff, diffGray, ColorConversionCodes.BGR2GRAY);

// 差分を強調表示するために二値化する
using var result = new Mat();
Cv2.Threshold(diffGray, result, thresh: 20, maxval: 255, type: ThresholdTypes.Binary);

Cv2.ImWrite("diff_result.png", result);
Console.WriteLine("差分結果を diff_result.png に保存しました。");

カラー差分はチャンネルごとに |src1 - src2| が計算されます。その後グレースケール変換して二値化することで、色味の違いも含めた差異箇所を抽出できます。外観検査など、微細な色差を検出したいケースに有効です。

つまずきポイント

⚠️ サイズ・型が一致していないと例外になる

src1 と src2 のサイズ(幅・高さ)、チャンネル数、深度(CV_8U など)がすべて一致していないと OpenCVException が発生します。異なるソースから画像を取得する場合は事前に確認してください。

// サイズ確認の例
if (src1.Size() != src2.Size() || src1.Type() != src2.Type())
{
    Console.WriteLine("画像のサイズまたは型が一致しません。");
    return;
}

⚠️ Mat の Dispose 漏れに注意

Cv2.Absdiff で生成する dst の Mat は using で必ず解放してください。ループ内で差分計算を繰り返す場合(動画フレーム処理など)、using を省略するとネイティブメモリが蓄積してプロセスが肥大化します。

// ✅ 正しい書き方
using var diff = new Mat();
Cv2.Absdiff(src1, src2, diff);

// ❌ NG: Dispose されない
var diff = new Mat();
Cv2.Absdiff(src1, src2, diff);
// diff.Dispose() を忘れるとリーク

⚠️ Absdiff と単純減算の違い

C++ の cv::subtract や Mat の - 演算子は符号付き計算のため、負の差分はクリッピング(0)されます。Absdiff は必ず非負の絶対差を返すため、どちらが「明るい」かにかかわらず差分を検出できます。背景差分には Absdiff を使うのが正解です。

関連する関数

  • Cv2.Threshold — 差分画像を二値化してマスクを作る定番の後処理
  • Cv2.InRange — 色範囲指定でマスクを作る別アプローチ → 使い方記事
  • Cv2.ConnectedComponentsWithStats — 差分マスクからラベリングで領域解析 → 使い方記事
  • Cv2.CalcHist — 差分画像のヒストグラムで変化量を定量化 → 使い方記事

まとめ

Cv2.Absdiff はピクセル単位の絶対差分を1行で計算できるシンプルかつ強力な関数です。背景差分・外観検査・フレーム比較など、差異検出が必要な場面で幅広く使えます。Cv2.Threshold や Cv2.ConnectedComponentsWithStats と組み合わせることで、実用的な変化検出パイプラインを素早く構築できます。

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

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

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