cv::split / cv::merge の使い方【OpenCV/C++】〜チャンネルを分解・結合する〜

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

cv::split / cv::merge の使い方【OpenCV/C++】〜チャンネルを分解・結合する〜

cv::split は多チャンネルの Mat を1チャンネルずつに分解し、cv::merge は複数の1チャンネル Mat を1枚にまとめます。特定のチャンネルだけ処理したいとき、あるいはアルファチャンネルを付与したいときに必須の関数ペアです。

最小の呼び出し形は次のとおりです。

std::vector<cv::Mat> planes;
cv::split(src, planes);      // BGR → planes[0]=B, planes[1]=G, planes[2]=R
cv::merge(planes, dst);      // 再結合

動作環境

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

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


基本の使い方

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

int main()
{
    // BGR 3チャンネル画像を読み込む
    cv::Mat src = cv::imread("input.jpg");
    if (src.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return 1;
    }

    // ── split: BGR → 3枚の1チャンネル画像 ──
    std::vector<cv::Mat> planes;
    cv::split(src, planes);
    // planes[0] = B チャンネル
    // planes[1] = G チャンネル
    // planes[2] = R チャンネル

    // B チャンネルだけ0にしてから merge で再結合
    planes[0] = cv::Mat::zeros(src.size(), CV_8UC1);

    cv::Mat dst;
    cv::merge(planes, dst);

    cv::imwrite("output.jpg", dst);

    std::cout << "src  type: " << src.type()        << std::endl; // 16 (CV_8UC3)
    std::cout << "B    type: " << planes[0].type()  << std::endl; // 0  (CV_8UC1)
    std::cout << "dst  type: " << dst.type()        << std::endl; // 16 (CV_8UC3)
    return 0;
}

実行結果

src  type: 16
B    type: 0
dst  type: 16

type() が返す数値は CV_8UC3 = 16、CV_8UC1 = 0 に対応しています。split 後の各プレーンが確かに1チャンネルになっていることを確認できます。出力画像はBチャンネルが除去され、GとRだけが残った黄みがかった画像になります。

行ごとの解説

行 ポイント
cv::split(src, planes) planes は std::vector<cv::Mat> でチャンネル数分の要素に展開される
planes[0] = cv::Mat::zeros(...) 1チャンネル分だけ差し替えられる
cv::merge(planes, dst) planes の要素数がそのまま出力のチャンネル数になる

引数と戻り値

cv::split

引数 型 説明
src const cv::Mat& 入力多チャンネル画像
mvbegin または mv cv::Mat* または std::vector<cv::Mat>& 出力先。チャンネル数と同じ要素数が確保される

戻り値: なし(void)

cv::merge

引数 型 説明
mv const std::vector<cv::Mat>& または const cv::Mat* 結合する1チャンネル画像の配列
count size_t ポインタ版を使う場合の要素数
dst cv::Mat& 出力画像(チャンネル数 = mv の要素数)

戻り値: なし(void)


実践例

例1: アルファチャンネルを追加して BGRA 画像に変換する

PNG などアルファ付き形式で書き出すときの定番パターンです。

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

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

    // BGR → 3枚に分解
    std::vector<cv::Mat> planes;
    cv::split(bgr, planes);

    // 全画素が255(完全不透明)のアルファチャンネルを作る
    cv::Mat alpha(bgr.size(), CV_8UC1, cv::Scalar(255));

    // B, G, R, A の順で結合 → BGRA
    planes.push_back(alpha);
    cv::Mat bgra;
    cv::merge(planes, bgra);

    cv::imwrite("output.png", bgra); // PNG なら4チャンネルを保存できる

    std::cout << "bgra channels: " << bgra.channels() << std::endl; // 4
    return 0;
}
bgra channels: 4

例2: 特定チャンネルにだけフィルタを適用する

Gチャンネルだけガウシアンブラーをかけて再結合する例です。チャンネルごとに異なる処理を施せるのが split/merge の強みです。

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

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

    std::vector<cv::Mat> planes;
    cv::split(src, planes);

    // G チャンネル(planes[1])だけブラーをかける
    cv::GaussianBlur(planes[1], planes[1], cv::Size(15, 15), 0);

    cv::Mat dst;
    cv::merge(planes, dst);

    cv::imwrite("output.jpg", dst);
    std::cout << "完了" << std::endl;
    return 0;
}
完了

Gチャンネルだけぼやけた、やや独特な色感の出力画像が得られます。実務では、ノイズが特定チャンネルに集中している場合の選択的フィルタリングに応用できます。


つまずきポイント

⚠️ planes の事前サイズ確保は不要

cv::split は std::vector<cv::Mat> を渡せば内部で resize してくれます。planes.resize(3) を手動でやる必要はなく、空の vector を渡すだけでOKです。逆に手動で要素を積んでいると予期しない動作になることがあるので、cv::split の直前に clear() するか新しい vector を用意してください。

⚠️ cv::merge に渡す全要素のサイズと型が揃っていないと assertion で落ちる

merge は「同じサイズ・同じ深度(CV_8U など)の1チャンネル画像」を前提とします。異なるサイズの Mat を混ぜたり、CV_8UC1 と CV_32FC1 を混在させると実行時にアサーションエラーになります。アルファチャンネルを手動で作る際は元画像と同じ size() と深度を確認してください。

⚠️ チャンネル順は BGR(OpenCV の読み込みデフォルト)

cv::imread は RGB ではなく BGR 順で読み込みます。planes[0] は B、planes[2] は R です。RGBだと思って planes[0] をRチャンネルと誤解するのは典型的なバグ源です。RGB順が必要な場合は cv::cvtColor(src, src, cv::COLOR_BGR2RGB) を先に行ってから split してください。


関連する関数


まとめ

cv::split はBGR画像を1チャンネルずつ分解し、cv::merge で再結合します。チャンネル単位の加工・フィルタリング・アルファ付与など、画像処理の実装で非常に出番が多い関数ペアです。型・サイズの不一致によるアサーションエラーに注意しつつ、std::vector<cv::Mat> を使った呼び出し形で確実に扱ってください。

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

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

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

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