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_and → bitwise_or の組み合わせは、アルファチャネルを持たない画像の合成でよく使われるイディオムです。
つまずきポイント
⚠️ mask は必ず CV_8UC1(1チャネル8ビット)
mask 引数にカラー画像(CV_8UC3)を渡すと実行時に assertion エラーになります。
OpenCV Error: Assertion failed (mask.empty() || mask.type() == CV_8UC1)
cv::inRange や cv::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::inRange — HSV などの範囲でマスクを生成する。
bitwise_andとの組み合わせで色抽出の定番パターンを形成します - cv::threshold — 輝度でマスクを生成する。
bitwise_notと合わせてロゴ合成に使えます(実践例 2 参照) - cv::matchTemplate の使い方【OpenCV/C++】〜テンプレートマッチングで物体位置を検出する〜 — マッチング結果を閾値処理してマスクを作り、
bitwise_andで切り抜くフローに接続できます - cv::calcHist の使い方【OpenCV/C++】〜ヒストグラムを計算する〜 — マスクを
calcHistの第5引数に渡すと特定領域のヒストグラムだけを計算できます
まとめ
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)を開発しています。顔認証システムへの組み込み実績多数。
