Cv2.Split / Cv2.Merge の使い方【OpenCV/C#(OpenCvSharp)】〜チャンネルを分解・結合する〜
Cv2.Split は BGR などのマルチチャンネル画像を各チャンネルの Mat に分解し、Cv2.Merge は複数の単チャンネル Mat を1枚のマルチチャンネル画像に結合します。特定チャンネルだけ処理を加えてから再合成する、という実務でよく出るパターンの基本関数です。
最小の使い方はこうなります。
Mat[] channels = Cv2.Split(src); // BGR → B, G, R の3枚に分解
// channels[2] が R チャンネル
Cv2.Merge(channels, dst); // 3枚を再合成
動作環境
- OpenCvSharp4(OpenCV 4.x 系)
- NuGet:
OpenCvSharp4+OpenCvSharp4.runtime.win(Windows 実行時) - .NET 8 コンソールアプリ(トップレベルステートメント)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
BGR 画像を Split で分解し、B チャンネルだけゼロにしてから Merge で再合成するサンプルです。
using OpenCvSharp;
// 64x64 の BGR カラー画像を生成(赤 = (B:0, G:0, R:255))
using var src = new Mat(64, 64, MatType.CV_8UC3, new Scalar(0, 0, 255));
// --- Split: チャンネルごとの Mat 配列を得る ---
Mat[] channels = Cv2.Split(src);
// channels[0] = B, channels[1] = G, channels[2] = R
try
{
// B チャンネルをゼロクリア(R チャンネルは変更しない)
channels[0].SetTo(Scalar.All(0));
// --- Merge: チャンネルを再合成 ---
using var dst = new Mat();
Cv2.Merge(channels, dst);
// 各チャンネルの代表画素値を表示
Console.WriteLine($"src [0,0] = {src.At<Vec3b>(0, 0)}");
Console.WriteLine($"dst [0,0] = {dst.At<Vec3b>(0, 0)}");
Console.WriteLine($"B channel mean = {Cv2.Mean(channels[0]).Val0}");
Console.WriteLine($"G channel mean = {Cv2.Mean(channels[1]).Val0}");
Console.WriteLine($"R channel mean = {Cv2.Mean(channels[2]).Val0}");
// ファイルに保存して確認
Cv2.ImWrite("split_merge_result.png", dst);
Console.WriteLine("split_merge_result.png を保存しました");
}
finally
{
// Split が返す Mat[] は自分で Dispose する
foreach (var ch in channels)
ch.Dispose();
}
実行すると以下のように表示されます。
src [0,0] = (0, 0, 255)
dst [0,0] = (0, 0, 255)
B channel mean = 0
G channel mean = 0
R channel mean = 255
split_merge_result.png を保存しました
コード解説
| 行の処理 | ポイント |
|---|---|
Cv2.Split(src) |
戻り値は Mat[]。要素数は src.Channels() と一致する |
channels[0].SetTo(...) |
分解後の Mat は独立しているので直接書き換え可能 |
Cv2.Merge(channels, dst) |
入力配列の順序が出力のチャンネル順になる |
foreach の Dispose |
Split が返す各 Mat はネイティブメモリを持つため解放必須 |
引数と戻り値
Cv2.Split
Mat[] Cv2.Split(Mat src)
void Cv2.Split(Mat src, out Mat[] mv) // 別オーバーロード
| 引数 | 型 | 説明 |
|---|---|---|
src |
Mat |
入力マルチチャンネル画像(1〜4ch) |
| 戻り値 | Mat[] |
チャンネル数と同じ長さの単チャンネル Mat 配列 |
Cv2.Merge
void Cv2.Merge(Mat[] mv, Mat dst)
void Cv2.Merge(IEnumerable<Mat> mv, Mat dst)
| 引数 | 型 | 説明 |
|---|---|---|
mv |
Mat[] / IEnumerable<Mat> |
結合する単チャンネル Mat の配列(全要素のサイズ・型が同一であること) |
dst |
Mat |
出力マルチチャンネル画像(mv.Length チャンネル) |
実践例
例1: Hチャンネルだけ処理して色相を回転させる(HSV)
HSV に変換してから Split し、H チャンネルに一定値を加算して色相をシフトする典型パターンです。
using OpenCvSharp;
// サンプル画像を読み込む(BGR)
using var src = Cv2.ImRead("input.png");
if (src.Empty()) { Console.WriteLine("画像が読み込めません"); return; }
// BGR → HSV
using var hsv = new Mat();
Cv2.CvtColor(src, hsv, ColorConversionCodes.BGR2HSV);
// チャンネル分解
Mat[] channels = Cv2.Split(hsv);
try
{
// H チャンネル(0〜179)を 90 加算して色相を 180° 回転
// 飽和演算では 180 を超えるので、convertTo + 剰余 or AddWeighted で調整
using var hShifted = new Mat();
Cv2.Add(channels[0], new Mat(channels[0].Size(), channels[0].Type(), Scalar.All(90)), hShifted);
// 179 を超えた分を折り返す(0〜179 にクランプ)
// OpenCV の HSV は H=0〜179 なので 180 で mod
using var hMod = new Mat();
Cv2.Subtract(hShifted,
new Mat(hShifted.Size(), hShifted.Type(), Scalar.All(0)), hMod); // コピー
// 簡易折り返し: 180以上の画素を -180
using var mask = new Mat();
Cv2.Threshold(hShifted, mask, 179, 255, ThresholdTypes.Binary);
using var correction = new Mat(hShifted.Size(), hShifted.Type(), Scalar.All(180));
hShifted.CopyTo(hMod);
Cv2.Subtract(hMod, correction, hMod, mask);
// 差し替えて Merge
channels[0].Dispose();
channels[0] = hMod.Clone();
using var hsvShifted = new Mat();
Cv2.Merge(channels, hsvShifted);
// HSV → BGR に戻す
using var dst = new Mat();
Cv2.CvtColor(hsvShifted, dst, ColorConversionCodes.HSV2BGR);
Cv2.ImWrite("hue_shifted.png", dst);
Console.WriteLine("hue_shifted.png を保存しました");
}
finally
{
foreach (var ch in channels) ch.Dispose();
}
例2: R チャンネルのヒストグラムを計算する
Split で取り出した単チャンネル Mat をそのまま Cv2.CalcHist に渡せます。チャンネルごとのヒストグラム分析でよく使うパターンです。詳しくは Cv2.CalcHist の使い方【OpenCV/C#(OpenCvSharp)】〜ヒストグラムを計算する〜も参照してください。
using OpenCvSharp;
using var src = Cv2.ImRead("input.png");
if (src.Empty()) { Console.WriteLine("画像が読み込めません"); return; }
Mat[] channels = Cv2.Split(src); // channels[0]=B, [1]=G, [2]=R
try
{
// R チャンネル(channels[2])のヒストグラム
using var hist = new Mat();
int[] histSize = { 256 };
Rangef[] ranges = { new Rangef(0, 256) };
Cv2.CalcHist(
images: new[] { channels[2] },
channels: new[] { 0 }, // 単チャンネルなので 0
mask: null,
hist: hist,
dims: 1,
histSize: histSize,
ranges: ranges
);
// ヒストグラムの最大ビン値を取示
Cv2.MinMaxLoc(hist, out _, out double maxVal);
Console.WriteLine($"R チャンネル ヒストグラム最大値: {maxVal}");
}
finally
{
foreach (var ch in channels) ch.Dispose();
}
つまずきポイント
⚠️ Cv2.Split が返す Mat[] は自分で Dispose する
Cv2.Split が返す Mat[] の各要素はネイティブメモリを持つため、使い終わったら確実に Dispose しなければなりません。using 構文は配列要素には直接使えないため、try/finally で foreach Dispose するか、ヘルパーメソッドでラップするのが安全です。
// NG: channels が GC まかせで Dispose されない
Mat[] channels = Cv2.Split(src);
Cv2.Merge(channels, dst);
// → ネイティブメモリリーク
// OK: finally で確実に解放
try { ... }
finally { foreach (var ch in channels) ch.Dispose(); }
⚠️ Merge に渡す Mat はサイズ・型が完全一致していること
Cv2.Merge は渡すすべての Mat のサイズと要素型(MatType)が同一でなければなりません。サイズや型が異なると実行時例外が発生します。Split 後に一部チャンネルを Clone して差し替えるときは型変換しないよう注意してください。
⚠️ 4チャンネル(BGRA)画像の Split 結果は4要素
PNG など BGRA 形式で読み込んだ場合、Cv2.Split の戻り配列は [B, G, R, A] の4要素になります。3要素前提のコードに渡すと Cv2.Merge でチャンネル数ミスマッチになるため、src.Channels() で要素数を確認してから処理してください。
関連する関数
- Cv2.CvtColor — BGR/HSV/Lab など色空間変換。Split/Merge と組み合わせてチャンネル単位処理を行う際の必須関数
- Cv2.InRange の使い方【OpenCV/C#(OpenCvSharp)】〜色範囲でマスクを作る(色抽出)〜 — HSV に変換して Split 後に InRange で色抽出するパターンとセットで学ぶ
- Cv2.CalcHist の使い方【OpenCV/C#(OpenCvSharp)】〜ヒストグラムを計算する〜 — Split で取り出した単チャンネル Mat を直接渡してチャンネルごとのヒストグラムを取る
- Cv2.MixChannels — 特定チャンネルだけを選択的にコピーしたい場合の高機能版
まとめ
Cv2.Split で BGR 画像を単チャンネル Mat に分解し、処理後に Cv2.Merge で再合成するパターンは色空間操作・チャンネル別フィルタリングの基本です。Split が返す Mat[] の各要素は必ず Dispose し、Merge に渡す Mat のサイズ・型を揃えることが実装上の要点です。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

