catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

OpenCVでアルファブレンディング

OpenCVで画像同士を重ね合わせるとき、アルファブレンディングができたら演出の幅が広がりそうということで試してみた。

 

まずは実験ということで、文字を記述した画像を作っておいて、これを本来の画像にオーバーレイ表示する手法を取ることにした。

とりあえず加算半透明で描画する。手順はこんな感じ

  1. 処理対象の画像を読み込み
  2. オーバーレイ用の画像に文字をかく
  3. アルファブレンディング用のアルファ値の準備
  4. 元画像に重ね合わせる

 では順を追って。

1, 処理対象の画像を読み込み

    if( (src = imread(filename,1)).data == 0 )return -1;

 OpenCvの関数を呼び出すだけ。srcに目的の画像が格納される。

ちなみにsrcはcv::Mat型

 

2, オーバーレイ用の画像に文字を書く

ここでは2つのステップで画像に処理を行う。

  1. オーバーレイ用の画像を準備して
  2. オーバーレイ用の画像に文字を書く

オーバーレイ用の画像は入力画像とフォーマットを合わせておく。

RGB形式という前提のコード。

 

	// 黒色で塗りつぶされた画像を生成する
	// RGBフォーマット
	// 画像サイズは読み込んだ画像と同じサイズ
	cv::Mat	overlay(cv::Size(src.cols, src.rows), CV_8UC3, cv::Scalar(0,0,0) );
	cv::split(overlay, mv);	// yuvの各チャネルに分解する

	// テキストを描画する(全プレーンにまとめて)
	// RGBのすべてのチャネルに対して同じ文字をかいていく。
	// ※この例ではチャネルの並び順がRGBではなくBGRになっている。
	int col_r = 0;
	int col_g = 255;
	int col_b = 0;
	cv::putText(mv[0], "OpenCV", cv::Point(50,50), cv::FONT_HERSHEY_SIMPLEX, 2.0, col_r, 2, CV_AA);
	cv::putText(mv[1], "OpenCV", cv::Point(50,50), cv::FONT_HERSHEY_SIMPLEX, 2.0, col_g, 2, CV_AA);
	cv::putText(mv[2], "OpenCV", cv::Point(50,50), cv::FONT_HERSHEY_SIMPLEX, 2.0, col_b, 2, CV_AA);

 

3, アルファブレンディング用のアルファ値の準備

アルファ値はオーバーレイ用の画像から取ってくることにする。

文字を半透明にして重ね合わせるために。

ここでのポイントは2つ。

  1. 後のMat同士の計算使うために、オーバーレイ画像と同数のチャンネルとサイズをもつアルファ情報を準備する。
  2. アルファ値は0.0~1.0までの範囲に正規化する(正規化しておかないと、重ね合わせたときの結果が期待しないものになる)

 

 

	// アルファチャネルを作る。
	cv::Mat	alpha32f;
	mv[1].convertTo(alpha32f, CV_32FC1);							// アルファチャンネルの値を浮動小数点型に変換

	// アルファチャンネルの正規化を行う
	cv::normalize(alpha32f, alpha32f, 0.0f, 1.0f, cv::NORM_MINMAX);	// アルファチャンネルを0-1に正規化する

	// 元画像と同じサイズをもつアルファ画像を作成する
	cv::Mat	alpha(cv::Size(src.cols, src.rows), CV_8UC3, cv::Scalar(0,0,0) );
	std::vector alpha_mv;
	cv::split(alpha, alpha_mv);							// アルファ画像をRGBの各チャネルに分解する
	alpha_mv[0] = alpha_mv[1] = alpha_mv[2] = alpha32f;	// アルファ値をRGB各プレーンに反映させる

	// アルファチャネルもソース画像, オーバーレイ画像と同じ数のチャネルが必要。
	cv::Mat alpha_tmp, alpha32fc3;
	cv::merge(alpha_mv, alpha_tmp);    // RGB各要素のアルファをマージして1つのMatにする
	alpha_tmp.convertTo(alpha32fc3, CV_32FC3);

 

4, 元画像に重ね合わせる

最後に、作った画像同士をアルファブレンディングで合成して、ファイルに書き出す。

式で表すと「出力画像 = 元画像 + (文字を書いた画像*アルファ値)」とする。

これにより加算半透明が実現できる。

ほかの表現を使いたい場合はアルファ合成している箇所を変更してあげればよい。

 

 

	// オーバーレイ画像を、入力画像と同じフォーマットにする
	cv::Mat overlay32fc3;
	cv::Mat	overlayTmp;
	cv::merge(mv, overlayTmp);						// 個別のplaneで処理していたが、ここでマージする。
	overlayTmp.convertTo(overlay32fc3, CV_32FC3);	// 32bit, 3chに変換する

	// 元画像に重ね合わせるために、floatへ変換しとく
	cv::Mat	img1_32f;
	src.convertTo(img1_32f, CV_32FC3);

	// アルファ値を仕様して、元画像に重ね合わせを行う
	// この演算では、全ての演算対象が同じチャネル、サイズ、次元をもっている必要がある。
	// (変数: img1_32f, overlay32fc3, alpha32fc3)
	cv::Mat blend32f = img1_32f - overlay32fc3.mul(alpha32fc3);

	cv::Mat blend;
	blend32f.convertTo(blend, CV_8UC3);

	// 確認のためにファイルに出力する
	imwrite("overlay.jpg", blend);

 

これで"OpenCV"という文字が加算半透明で付け加えられた画像が出力される。

Matのmergeとsplitなど無駄かもしれない手順を踏んでいるが、とりあえずは目的達成ということで。