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::mixChannels:
split+mergeを1回の呼び出しで行える高度版。コピーするチャンネルをインデックスで細かく指定できます。チャンネル数が多い場合や部分コピーにはmixChannelsが効率的です。 - cv::bitwise_and の使い方【OpenCV/C++】〜ビット演算とマスク処理〜:
splitで抽出した1チャンネルにマスク処理を組み合わせるケースで頻繁に使います。 - cv::add / addWeighted の使い方【OpenCV/C++】〜画像の加算・ブレンド合成〜: チャンネルを分解してから加算処理を行い、
mergeで戻すパターンに活用できます。 - cv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜: チャンネルごとの差分を取りたい場合に
splitと組み合わせます。
まとめ
cv::split はBGR画像を1チャンネルずつ分解し、cv::merge で再結合します。チャンネル単位の加工・フィルタリング・アルファ付与など、画像処理の実装で非常に出番が多い関数ペアです。型・サイズの不一致によるアサーションエラーに注意しつつ、std::vector<cv::Mat> を使った呼び出し形で確実に扱ってください。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

