openFrameworks + OpenCVで映像合成

経緯

openFrameworksとOpenCVについて、2つの画像をリアルタイムで合成するためにはどうしたらいいかというご質問をメールでいただいたので、解説していこうと思います。

前提

まず、次のような画像があるとします。
ofxCvGrayscaleImage binaryImg : どこに画像を当てはめるかを白黒の2値画像で表したもの
ofxCvColorImage sampleImg1 : binaryImgの白に当てはめる画像
ofxCvColorImage sampleImg2 : binaryImgの黒に当てはめる画像
ofxCvColorImage compositeImg : 実際に二種類の画像を合成したもの

コード

unsigned char * binaryImgPixels = binaryImg.getPixels();
unsigned char * sampleImg1Pixels = sampleImg1.getPixels();
unsigned char * sampleImg2Pixels = sampleImg2.getPixels();

int nPixels = camWidth*camHeight;

unsigned char * compositeImgPixels = new unsigned char[nPixels*3];

for(int i=0; i<nPixels; i++){
    if(binaryImgPixels[i]==255){
        compositeImgPixels[3*i] = sampleImg1Pixels[3*i];
        compositeImgPixels[3*i+1] = sampleImg1Pixels[3*i+1];
        compositeImgPixels[3*i+2] = sampleImg1Pixels[3*i+2];
    } else {
        compositeImgPixels[3*i] = sampleImg2Pixels[3*i];
        compositeImgPixels[3*i+1] = sampleImg2Pixels[3*i+1];
        compositeImgPixels[3*i+2] = sampleImg2Pixels[3*i+2];
    }
}

compositeImg.setFromPixels(compositeImgPixels, camWidth, camHeight);

以上で二種類の画像が合成されたcompositeImgが作成されます。

解説

for文の中に3*i, 3*i+1, 3*i+2というものがありますが、それぞれ、i番目のピクセルのRとGとBの値が入っている配列のインデックスを表しています。カラー画像のピクセル情報は配列に(R,G,B,R,G,B,…)という風に入っているので、i番目のピクセルに色をコピーしたい場合は3*i(R)と3*i+1(G)と3*i+2(B)をすべてコピーしなくてはなりません。

実践例

次の動画は応用例です。

背景画像をあらかじめ取得しておき、背景との差分をとって2値画像(binaryImg)を作成します。さらにカメラから取得したそのままの画像(sampleImg1)と、背景画像を少し拡大した画像(sampleImg2)を合成することで、光の屈折を表現し、透明人間がいるように表現しています。

以前、掲載したopenFrameworks & OpenCVでColor Tracking(色追跡)の記事と組み合わせて、何かを作りたい場合、ここでいうbinaryImgPixelscolorTrackedPixelsRedに対応します。

まとめ

  • 画像の配列は[R, G, B, R, G, B, …]という形で入っている。
  • i番目のピクセルの値を取得/格納するためには3i, 3i+1, 3i+2番目の値を参照する。

画像認識を使うといろいろ面白いことができるので、是非、やってみてはいかがでしょうか。