cv::add / addWeighted の使い方【OpenCV/C++】〜画像の加算・ブレンド合成〜

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

cv::add / addWeighted の使い方【OpenCV/C++】〜画像の加算・ブレンド合成〜

cv::add は2枚の画像をピクセル単位で加算し、cv::addWeighted は重みを指定してブレンド合成する関数です。フェード・透かし・オーバーレイなど、現場でよく使う合成処理を飽和演算(クランプ)付きで安全に行えます。

最小の使い方はこうです。

cv::add(src1, src2, dst);
cv::addWeighted(src1, alpha, src2, beta, gamma, dst);

動作環境

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

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


基本の使い方

cv::add — 単純加算

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

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

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

    // サイズ・型を統一する(異なる場合は assertion で落ちる)
    cv::resize(src2, src2, src1.size());

    cv::Mat dst;
    // ピクセル値を加算。255 を超えた部分は 255 にクランプされる
    cv::add(src1, src2, dst);

    cv::imwrite("add_result.png", dst);
    std::cout << "add_result.png を保存しました" << std::endl;
    return 0;
}

実行結果

add_result.png を保存しました

2枚の画像が重ね合わされた画像が保存されます。明るい領域が増え、白飛びしやすい(飽和演算)のが cv::add の特徴です。


cv::addWeighted — 重み付きブレンド合成

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

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

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

    cv::resize(src2, src2, src1.size());

    cv::Mat dst;
    double alpha = 0.7; // src1 の重み
    double beta  = 0.3; // src2 の重み(alpha + beta = 1.0 が自然なブレンド)
    double gamma = 0.0; // 加算するオフセット値

    // dst = src1 * alpha + src2 * beta + gamma
    cv::addWeighted(src1, alpha, src2, beta, gamma, dst);

    cv::imwrite("blend_result.png", dst);
    std::cout << "blend_result.png を保存しました" << std::endl;
    return 0;
}

実行結果

blend_result.png を保存しました

alpha=0.7, beta=0.3 の場合、src1 が 70%・src2 が 30% の割合でブレンドされた画像が保存されます。alpha + beta = 1.0 にすると全体の輝度が変化しないため、自然な合成になります。


引数と戻り値

cv::add

引数 説明
src1 InputArray 入力画像1
src2 InputArray 入力画像2(スカラー値も可)
dst OutputArray 出力画像
mask InputArray 処理対象のマスク(省略可、デフォルト noArray()
dtype int 出力の深度(省略可、デフォルト -1 で入力と同じ)

戻り値: なし(void

cv::addWeighted

引数 説明
src1 InputArray 入力画像1
alpha double src1 に掛ける重み
src2 InputArray 入力画像2
beta double src2 に掛ける重み
gamma double 結果に加算するオフセット値
dst OutputArray 出力画像
dtype int 出力の深度(省略可、デフォルト -1

戻り値: なし(void

演算式: dst = src1 * alpha + src2 * beta + gamma


実践例

透かし(ウォーターマーク)の合成

ロゴ画像を薄く重ねてウォーターマークを合成する典型的なパターンです。

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

int main() {
    cv::Mat base = cv::imread("photo.png");
    cv::Mat logo = cv::imread("logo.png");

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

    // ロゴを右下に配置するための ROI を設定
    int x = base.cols - logo.cols - 20;
    int y = base.rows - logo.rows - 20;

    if (x < 0 || y < 0) {
        std::cerr << "ロゴが大きすぎます" << std::endl;
        return 1;
    }

    cv::Mat roi = base(cv::Rect(x, y, logo.cols, logo.rows));

    // ROI 領域にのみブレンド(ロゴを 30% の透明度で合成)
    cv::addWeighted(roi, 1.0, logo, 0.3, 0.0, roi);

    cv::imwrite("watermark_result.png", base);
    std::cout << "watermark_result.png を保存しました" << std::endl;
    return 0;
}

cv::Mat roi = base(cv::Rect(...)) は base との共有メモリなので、roi への書き込みがそのまま base に反映されます。ROI を使うことで、ループなしに特定領域だけ合成できます。


gamma によるコントラスト・輝度補正

cv::addWeightedgamma は「自分自身にブレンド + オフセット」の形で輝度調整にも応用できます。

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

int main() {
    cv::Mat src = cv::imread("photo.png");
    if (src.empty()) {
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return 1;
    }

    cv::Mat dst;
    // alpha=1.2 でコントラスト強調、gamma=-30 で全体を暗くオフセット
    cv::addWeighted(src, 1.2, src, 0.0, -30.0, dst);

    cv::imwrite("contrast_result.png", dst);
    std::cout << "contrast_result.png を保存しました" << std::endl;
    return 0;
}

src2 に同じ画像を渡し beta=0.0 にすると、実質 dst = src * alpha + gamma の線形変換になります。輝度・コントラストの粗調整に手軽に使えます。


つまずきポイント

⚠️ サイズ・型が一致しないと assertion で落ちる

cv::add / cv::addWeighted は src1 と src2 のサイズ・チャンネル数・深度が一致していないと実行時に assertion エラーになります。

OpenCV Error: Sizes of input arguments do not match

cv::resize でサイズを合わせ、チャンネル数は cv::cvtColor で揃えてから渡してください。グレースケール(CV_8UC1)とカラー(CV_8UC3)の混在は特に起きやすいミスです。


⚠️ alpha + beta の合計が 1.0 を超えると白飛びする

cv::addWeighted は内部で飽和演算を行うため、alpha + beta > 1.0 だと明るい領域が白飛びします。「自然なブレンド合成」を意図する場合は alpha + beta = 1.0 を守ってください。あえてハイキー表現にしたい場合を除き、まず合計を 1.0 に設定するところから始めるのが無難です。


⚠️ float 型(CV_32F)の画像に使う場合のクランプ挙動

入力が CV_32F の場合、飽和クランプは行われず 0〜255 の範囲を超えた値がそのままフロート値として格納されます。imwrite 前に convertToCV_8U に変換するか、出力を normalize してから保存してください。


関連する関数


まとめ

  • cv::add はピクセル値の飽和加算、cv::addWeighted は重み付きブレンド合成に使う。
  • alpha + beta = 1.0 にすることで自然な透過合成が実現できる。
  • サイズ・チャンネル数・深度の不一致は実行時エラーになるため、resizecvtColor で前処理を統一することが重要。

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

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

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

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