cv::bitwise_and の使い方【OpenCV/C++】〜ビット演算とマスク処理〜

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

cv::bitwise_and の使い方【OpenCV/C++】〜ビット演算とマスク処理〜

cv::bitwise_and は2枚の画像(または画像とスカラー)のピクセル値をビット単位で AND 演算する関数です。マスク画像と組み合わせることで、特定領域だけを切り抜く・背景を黒で塗りつぶすといった処理を1行で書けます。

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

cv::bitwise_and(src1, src2, dst, mask);

動作環境

  • OpenCV 4.x
  • ビルド例: g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main

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


基本の使い方

まず cv::bitwise_and のシンプルな動作を確認します。白黒の矩形画像を2枚生成し、AND を取る例です。

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

int main()
{
    // 300x300 の黒画像を2枚用意
    cv::Mat img1 = cv::Mat::zeros(300, 300, CV_8UC1);
    cv::Mat img2 = cv::Mat::zeros(300, 300, CV_8UC1);

    // img1: 左半分を白
    img1(cv::Rect(0, 0, 150, 300)).setTo(255);

    // img2: 上半分を白
    img2(cv::Rect(0, 0, 300, 150)).setTo(255);

    // AND 演算: 両方が白のピクセルだけ白になる
    cv::Mat dst;
    cv::bitwise_and(img1, img2, dst);

    cv::imwrite("result_and.png", dst);
    std::cout << "saved: result_and.png" << std::endl;

    return 0;
}

実行結果

saved: result_and.png

result_and.png を開くと、左上の 150×150 領域だけが白、それ以外は黒の画像が保存されています。AND 演算なので「両方とも 255(白)」の画素だけが 255 になり、片方でも 0 なら 0 になります。

行ごとの解説

  • cv::Mat::zeros で確実に全ピクセルが 0 の Mat を生成しています
  • img1(cv::Rect(...)).setTo(255) は ROI 参照を使った部分塗りつぶし(コピーではなく参照)
  • cv::bitwise_and(img1, img2, dst) の第4引数(mask)を省略すると全ピクセルが対象になります

引数と戻り値

cv::bitwise_and

引数 説明
src1 InputArray 入力画像1(任意の深度・チャネル数)
src2 InputArray 入力画像2(src1 と同じサイズ・型)
dst OutputArray 出力画像(src1 と同じサイズ・型)
mask InputArray ⚠️省略可。8UC1 のマスク画像。0 でないピクセルのみ演算結果を書き込む

戻り値: なし(void

ビット演算ファミリー

関数 演算 典型的な用途
cv::bitwise_and A & B マスクで領域を切り抜く
cv::bitwise_or A | B 2枚の画像を合成する
cv::bitwise_xor A ^ B 差分検出・パターン合成
cv::bitwise_not ~A マスクを反転させる

いずれも引数の構成は同じです。


実践例

実践例 1: カラー画像からマスクで任意領域を切り抜く

cv::inRange でHSV色空間の特定色を検出し、そのマスクで元画像を切り抜く王道パターンです。

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

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

    // HSV に変換して赤色領域をマスク化
    cv::Mat hsv;
    cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);

    // 赤色の HSV 範囲(例: H=0〜10 と 170〜180 の2領域を OR で合成)
    cv::Mat mask1, mask2, mask;
    cv::inRange(hsv, cv::Scalar(0, 100, 100),   cv::Scalar(10, 255, 255),  mask1);
    cv::inRange(hsv, cv::Scalar(170, 100, 100),  cv::Scalar(180, 255, 255), mask2);
    cv::bitwise_or(mask1, mask2, mask);  // 2つのマスクを合算

    // マスクで元画像を切り抜く
    cv::Mat result;
    cv::bitwise_and(src, src, result, mask);

    cv::imwrite("masked_red.png", result);
    std::cout << "saved: masked_red.png" << std::endl;

    return 0;
}

実行結果

saved: masked_red.png

赤色と判定されたピクセルのみが元の色で残り、それ以外は黒(0)になった画像が保存されます。cv::bitwise_and(src, src, result, mask) という src1 == src2 の書き方は「マスクで切り抜くだけ」のイディオムとして頻出します。


実践例 2: マスクを反転して前景・背景を分離合成する

ロゴ画像をカラー画像に合成するとき、cv::bitwise_not でマスクを反転させて2領域を別々に処理するパターンです。

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

int main()
{
    cv::Mat background = cv::imread("background.jpg");
    cv::Mat logo       = cv::imread("logo.png");        // 白背景・黒ロゴを想定

    if (background.empty() || logo.empty()) {
        std::cerr << "画像を読み込めませんでした" << std::endl;
        return 1;
    }

    // logo を background の左上に合わせるため ROI を取得
    cv::Rect roi_rect(0, 0, logo.cols, logo.rows);
    cv::Mat roi = background(roi_rect);

    // グレースケールのマスクを作成(白背景 → マスク白、黒ロゴ → マスク黒)
    cv::Mat logo_gray, mask, mask_inv;
    cv::cvtColor(logo, logo_gray, cv::COLOR_BGR2GRAY);
    cv::threshold(logo_gray, mask, 200, 255, cv::THRESH_BINARY);  // 白背景部分
    cv::bitwise_not(mask, mask_inv);                               // ロゴ部分

    // 背景の ROI からロゴ領域(黒)を除去
    cv::Mat bg_part;
    cv::bitwise_and(roi, roi, bg_part, mask);

    // ロゴだけを抽出
    cv::Mat logo_part;
    cv::bitwise_and(logo, logo, logo_part, mask_inv);

    // 合算して ROI に書き戻す
    cv::Mat combined;
    cv::bitwise_or(bg_part, logo_part, combined);
    combined.copyTo(roi);

    cv::imwrite("composited.jpg", background);
    std::cout << "saved: composited.jpg" << std::endl;

    return 0;
}

実行結果

saved: composited.jpg

background.jpg の左上に logo.png が合成された画像が保存されます。bitwise_andbitwise_or の組み合わせは、アルファチャネルを持たない画像の合成でよく使われるイディオムです。


つまずきポイント

⚠️ mask は必ず CV_8UC1(1チャネル8ビット)

mask 引数にカラー画像(CV_8UC3)を渡すと実行時に assertion エラーになります。

OpenCV Error: Assertion failed (mask.empty() || mask.type() == CV_8UC1)

cv::inRangecv::threshold の出力は自動的に CV_8UC1 になるので安全ですが、自作マスクを使う場合は型を確認してください。


⚠️ src1・src2・dst のサイズと型は完全一致が必須

チャネル数が違う(例: src1 が CV_8UC3 で src2 が CV_8UC1)と同じく assertion エラーになります。cv::cvtColor で変換してチャネルを揃えてから使いましょう。


⚠️ mask の 0 でない領域にのみ結果が書き込まれる

dst を事前に cv::Mat::zeros で初期化してから bitwise_and を呼ぶと「マスク外は黒」という状態になります。一方、dst を初期化せずに使い回すと、マスク外に前回の演算結果が残ることがあります。意図しない残像を防ぐために、dst = cv::Mat::zeros(src.size(), src.type()); を先に書く習慣をつけると安全です。


関連する関数


まとめ

  • cv::bitwise_and はピクセルのビット AND 演算で、第4引数 mask を渡すと指定領域のみに結果を書き込める
  • bitwise_and(src, src, dst, mask) という自己参照の書き方が「マスクで切り抜く」イディオムとして最頻出
  • bitwise_or / bitwise_not と組み合わせることで、アルファチャネルなしでも前景・背景分離合成が実現できる

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

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

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

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