openCVを使った動画像の処理について教えてください。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
relaxma

openCVを使った動画像の処理について教えてください。

#1

投稿記事 by relaxma » 14年前

はじめまして。
openCV1.0を使って動画像の処理、背景差分を使って背景と物体にわけ、背景にまた別の画像を貼り付ける仕様にしたいのですが、
行き詰ってしまいました。
openCVの公式サイトのサンプルプログラムをもとにして背景差分はできたのですがそこからどう別の画像を取り込めばよいのかわかりません。
かなりの初心者でほとんど知識はないのですが、詳しい回答をよろしくお願いします。

コード:

#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <stdio.h>
#define snprintf _snprintf
int 
main (int argc, char **argv)
{  
    int i, c, counter;  
    int INIT_TIME = 100;  
    int w = 0, h = 0;  
    double B_PARAM = 1.0 / 50.0;  
    double T_PARAM = 1.0 / 200.0;  
    double Zeta = 10.0;  
    CvCapture *capture = 0;  
    IplImage *frame = 0;  
    IplImage *av_img, *sgm_img;  
    IplImage *lower_img, *upper_img, *tmp_img;  
    IplImage *dst_img, *msk_img;  
    CvFont font;  char str[64];  

    // (1)コマンド引数によって指定された番号のカメラに対するキャプチャ構造体を作成する  
    if (argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0])))    
        capture = cvCreateCameraCapture (argc == 2 ? argv[1][0] - '0' : 0);  
    // (2)1フレームキャプチャし,キャプチャサイズを取得する.  
    frame = cvQueryFrame (capture);  w = frame->width;  h = frame->height; 

    // (3)作業用の領域を生成する  
    av_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
    sgm_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
    tmp_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
    lower_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
    upper_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
    dst_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 3);  
    msk_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 1);  
    // (4)背景の輝度平均の初期値を計算する  
    printf ("Background statistics initialization start\n");  
    cvSetZero (av_img);  for (i = 0; i < INIT_TIME; i++) {    
        frame = cvQueryFrame (capture);    
        cvAcc (frame, av_img);
    }  
    cvConvertScale (av_img, av_img, 1.0 / INIT_TIME); 
    // (5)背景の輝度振幅の初期値を計算する  
    cvSetZero (sgm_img);  
    for (i = 0; i < INIT_TIME; i++) {
        frame = cvQueryFrame (capture);    cvConvert (frame, tmp_img); 
        cvSub (tmp_img, av_img, tmp_img);    cvPow (tmp_img, tmp_img, 2.0);
        cvConvertScale (tmp_img, tmp_img, 2.0);    cvPow (tmp_img, tmp_img, 0.5);
        cvAcc (tmp_img, sgm_img);
    }  
    cvConvertScale (sgm_img, sgm_img, 1.0 / INIT_TIME);
    printf ("Background statistics initialization finish\n");
    // (6)表示用ウィンドウを生成する  
    cvInitFont (&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7);
    cvNamedWindow ("Input", CV_WINDOW_AUTOSIZE);
    cvNamedWindow ("Substraction", CV_WINDOW_AUTOSIZE); 
    // (7)取得画像から背景を分離するループ
    counter = 0;
    while (1) {
        frame = cvQueryFrame (capture);
        cvConvert (frame, tmp_img);    

        // (8)背景となりうる画素の輝度値の範囲をチェックする
        cvSub (av_img, sgm_img, lower_img);
        cvSubS (lower_img, cvScalarAll (Zeta), lower_img);
        cvAdd (av_img, sgm_img, upper_img);
        cvAddS (upper_img, cvScalarAll (Zeta), upper_img);
        cvInRange (tmp_img, lower_img, upper_img, msk_img);    
        // (9)輝度振幅を再計算する   
        cvSub (tmp_img, av_img, tmp_img); 
        cvPow (tmp_img, tmp_img, 2.0);
        cvConvertScale (tmp_img, tmp_img, 2.0);
        cvPow (tmp_img, tmp_img, 0.5);
        // (10)背景と判断された領域の背景の輝度平均と輝度振幅を更新する
        cvRunningAvg (frame, av_img, B_PARAM, msk_img);
        cvRunningAvg (tmp_img, sgm_img, B_PARAM, msk_img);
        cvSaveImage("vinci_result", image);
        IplImage *image = cvLoadImage("Apple.jpg", CV_LOAD_IMAGE_ANYCOLOR);
        // (11)物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
        cvNot (msk_img, msk_img);
        cvRunningAvg (tmp_img, sgm_img, T_PARAM, msk_img);
        // (12)物体領域のみを出力画像にコピーする(背景領域は黒)
        cvSetZero (dst_img);    cvCopy (frame, dst_img, msk_img); 
        // (13)処理結果を表示する    
        snprintf (str, 64, "%03d[frame]", counter);
        cvPutText (dst_img, str, cvPoint (10, 20), &font, CV_RGB (0, 255, 100));
        cvShowImage ("Input", frame);    cvShowImage ("Substraction", dst_img);
        counter++;    c = cvWaitKey (10);
        if (c == '\x1b')
            break;
    }  
    cvDestroyWindow ("Input");
    cvDestroyWindow ("Substraction");
    cvReleaseImage (&frame);
    cvReleaseImage (&dst_img);
    cvReleaseImage (&av_img); 
    cvReleaseImage (&sgm_img);
    cvReleaseImage (&lower_img); 
    cvReleaseImage (&upper_img); 
    cvReleaseImage (&tmp_img); 
    cvReleaseImage (&msk_img); 
    return 0;
}

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: openCVを使った動画像の処理について教えてください。

#2

投稿記事 by beatle » 14年前

現在は「物体」の形が分かっているということですよね。
そしたら、貼りつけたい背景画像の物体以外の部分を、物体の画像にコピーすればいいのではなかと思います。
違う言い方をすれば、物体の部分だけ透明にした背景画像を物体画像に重ねるということです。
具体的には2重for分で画像の高さ、幅だけループさせ、(x, y)が物体上かどうかをif文で判断しつつ
ピクセルをコピーすればとりあえず実装できると思います。

relaxma

Re: openCVを使った動画像の処理について教えてください。

#3

投稿記事 by relaxma » 14年前

beatle さんが書きました:現在は「物体」の形が分かっているということですよね。
そしたら、貼りつけたい背景画像の物体以外の部分を、物体の画像にコピーすればいいのではなかと思います。
なるほど、発想の逆転というわけですね。

しかし、やるべきことはわかったのですが、初心者中の初心者なため、それをどうすれば実行に移せるかがわかりません。
詳しく教えていただいてありがたいのですがさらに詳細を教えてはいただけないでしょうか。よろしくお願いします。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: openCVを使った動画像の処理について教えてください。

#4

投稿記事 by beatle » 14年前

relaxmaさんの書いたコードはパッと見複雑そうでしたので中身は一切見ていません。そんな回答ですので、的外れかもしれませんけれど。
以下擬似コードで書いてみます。
参考(画素の直接操作):http://opencv.jp/sample/basic_structures.html

コード:

IplImage* frontImage; // 物体の画像
IplImage* backImage; // 背景の画像

for (int y = 0; y < frontImage->height; ++y)
{
    for (int x = 0; x < frontImage->width; ++x)
    {
        if (frontImageの(x,y)要素が物体以外)
        {
            frontImageの(x,y) = backImageの(x,y);
        }
    }
}
このようにすれば、backImageの「物体以外の場所の画像」がfrontImageにコピーされます。

relaxma

Re: openCVを使った動画像の処理について教えてください。

#5

投稿記事 by relaxma » 14年前

beatle さんが書きました:

コード:

IplImage* frontImage; // 物体の画像
IplImage* backImage; // 背景の画像

for (int y = 0; y < frontImage->height; ++y)
{
    for (int x = 0; x < frontImage->width; ++x)
    {
        if (frontImageの(x,y)要素が物体以外)
        {
            frontImageの(x,y) = backImageの(x,y);
        }
    }
}
if (frontImageの(x,y)要素が物体以外),
frontImageの(x,y) = backImageの(x,y);
をソースコード上ではどう表せばいいのかわかりません。
そして、申し訳ないのですが、自分のコードもあまり合っている自信がないため見ていただきたいです。

それとfrontImage, backImageの識別子が見つからないときたのですが何を付け加えればこれは解決するのでしょうか。

いろいろ注文つけてあつかましいですが回答よろしくお願いします。

コード:

#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <stdio.h>
#define snprintf _snprintf

int main (int argc, char **argv)
{  
	int i, c, counter;  
	int INIT_TIME = 100;  
	int w = 0, h = 0;  
	double B_PARAM = 1.0 / 50.0;  
	double T_PARAM = 1.0 / 200.0;  
	double Zeta = 10.0;  
	CvCapture *capture = 0;  
	IplImage *frame = 0;  
	IplImage *av_img, *sgm_img;  
	IplImage *lower_img, *upper_img, *tmp_img;  
	IplImage *dst_img, *msk_img;  
	CvFont font;  
	char str[64];  
    IplImage* frontImage; // 物体の画像
    IplImage* backImage; // 背景の画像

	for (int y = 0; y < frontImage->height; ++y)
{
    for (int x = 0; x < frontImage->width; ++x)
    {
        if (frontImageの(x,y)要素が物体以外)
        {
            frontImageの(x,y) = backImageの(x,y);
        }
    }
} 


	// (1)コマンド引数によって指定された番号のカメラに対するキャプチャ構造体を作成する  
	if (argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0])))    
		capture = cvCreateCameraCapture (argc == 2 ? argv[1][0] - '0' : 0);  
	// (2)1フレームキャプチャし,キャプチャサイズを取得する.  
	frame = cvQueryFrame (capture);
	w = frame->width;
	h = frame->height; 

	// (3)作業用の領域を生成する  
	av_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
	sgm_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
	tmp_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
	lower_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
	upper_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);  
	dst_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 3);  
	msk_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 1);  
	// (4)背景の輝度平均の初期値を計算する  
	printf ("Background statistics initialization start\n");  
	cvSetZero (av_img);  
	for (i = 0; i < INIT_TIME; i++) {    
		frame = cvQueryFrame (capture);    
		cvAcc (frame, av_img);
	}  
	cvConvertScale (av_img, av_img, 1.0 / INIT_TIME); 
	// (5)背景の輝度振幅の初期値を計算する  
	cvSetZero (sgm_img);  
	for (i = 0; i < INIT_TIME; i++) {
		frame = cvQueryFrame (capture);
		cvConvert (frame, tmp_img); 
		cvSub (tmp_img, av_img, tmp_img);    
		cvPow (tmp_img, tmp_img, 2.0);
		cvConvertScale (tmp_img, tmp_img, 2.0);    
		cvPow (tmp_img, tmp_img, 0.5);
		cvAcc (tmp_img, sgm_img);
	}  
	cvConvertScale (sgm_img, sgm_img, 1.0 / INIT_TIME);
	printf ("Background statistics initialization finish\n");
	// (6)表示用ウィンドウを生成する  
	cvInitFont (&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7);
	cvNamedWindow ("Input", CV_WINDOW_AUTOSIZE);
	cvNamedWindow ("Substraction", CV_WINDOW_AUTOSIZE); 
	// (7)取得画像から背景を分離するループ
	counter = 0;
	while (1) {
		frame = cvQueryFrame (capture);
		cvConvert (frame, tmp_img);    

		// (8)背景となりうる画素の輝度値の範囲をチェックする
		cvSub (av_img, sgm_img, lower_img);
		cvSubS (lower_img, cvScalarAll (Zeta), lower_img);
		cvAdd (av_img, sgm_img, upper_img);
		cvAddS (upper_img, cvScalarAll (Zeta), upper_img);
		cvInRange (tmp_img, lower_img, upper_img, msk_img);    
		// (9)輝度振幅を再計算する   
		cvSub (tmp_img, av_img, tmp_img); 
		cvPow (tmp_img, tmp_img, 2.0);
		cvConvertScale (tmp_img, tmp_img, 2.0);
		cvPow (tmp_img, tmp_img, 0.5);
		// (10)背景と判断された領域の背景の輝度平均と輝度振幅を更新する
		cvRunningAvg (frame, av_img, B_PARAM, msk_img);
		cvRunningAvg (tmp_img, sgm_img, B_PARAM, msk_img);
		
		//cvSaveImage("vinci_result", image);
		//IplImage *image = cvLoadImage("Apple.jpg", CV_LOAD_IMAGE_ANYCOLOR);
		// (11)物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
		cvNot (msk_img, msk_img);
		cvRunningAvg (tmp_img, sgm_img, T_PARAM, msk_img);
		// (12)物体領域のみを出力画像にコピーする(背景領域は黒)
		cvSetZero (dst_img);    
		cvCopy (frame, dst_img, msk_img); 
		// (13)処理結果を表示する    
		snprintf (str, 64, "%03d[frame]", counter);
		cvPutText (dst_img, str, cvPoint (10, 20), &font, CV_RGB (0, 255, 100));
		cvShowImage ("Input", frame);    
		cvShowImage ("Substraction", dst_img);
		counter++;    
		c = cvWaitKey (10);
		if (c == '\x1b')
			break;
	}  
	cvDestroyWindow ("Input");
	cvDestroyWindow ("Substraction");
	cvReleaseImage (&frame);
	cvReleaseImage (&dst_img);
	cvReleaseImage (&av_img); 
	cvReleaseImage (&sgm_img);
	cvReleaseImage (&lower_img); 
	cvReleaseImage (&upper_img); 
	cvReleaseImage (&tmp_img); 
	cvReleaseImage (&msk_img); 
	return 0;
}

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: openCVを使った動画像の処理について教えてください。

#6

投稿記事 by beatle » 14年前

僕が示した参考URLは難しかったでしょうか?あそこに必要なやり方はほとんど書いてあるのですけれど。
参考URLに載っているサンプルによれば、IplImage* imgの座標(x,y)のデータは

コード:

img->imageData[img->widthStep * y + x * 3]; // B
img->imageData[img->widthStep * y + x * 3 + 1]; // G
img->imageData[img->widthStep * y + x * 3 + 2]; // R
のようにアクセスできます。これさえ分かれば、
「frontImageの(x,y)要素が物体以外」という条件式だって
「frontImageの(x,y) = backImageの(x,y);」という代入文だって書けるようになるはずです。

ここでのポイントは、結局のところ「1次元配列を用いて2次元配列っぽいことをする」ということです。
img->imageDataは1次元配列へのポインタなのですが、しかし(x,y)という2次元の座標で
画素の位置を指定しています。

例えばint型のheight*widthの要素数を持つ1次元配列dataが有ったとします。
1次元の配列にどうやって2次元のデータを入れるかというと
data[0 * width]~data[0 * width + width - 1] : (x,y) = (0,0), (1,0), (2,0), ..., (width-1,0) => 0行目のデータ
data[1 * width]~data[1 * widht + width - 1] : (x,y) = (0,1), (1,1), (2,1), ..., (width-1,1) => 1行目のデータ
data[2 * width]~data[2 * widht + width - 1] : (x,y) = (0,2), (1,2), (2,2), ..., (width-1,2) => 2行目のデータ
...
data[(height - 1) * width]~data[(height - 1) * widht + width - 1] : (x,y) = (0,height-1), (1,height-1), (2,height-1), ..., (width-1,height-1) => height - 1行目のデータ
という感じにします。つまり、座標(x,y)のデータにアクセスするには
data[y * width + x]
とすればいいのです。

物体のある領域が格納されているのがmsk_imgでしょうから、
「frontImageの(x,y)要素が物体以外」という条件を表すにはmsk_imgの(x,y)要素を調べればOKです。

relaxma

Re: openCVを使った動画像の処理について教えてください。

#7

投稿記事 by relaxma » 14年前

回答ありがとうございます。
なんと言いますか、これをもとのプログラムにどのように、どこに組み込むかが理解できないんですよね。

でもこれで少しやってみようと思います。
またわからなくなったら聴いてみたいと思います。

relaxma

Re: openCVを使った動画像の処理について教えてください。

#8

投稿記事 by relaxma » 14年前

すみません。基本がなっていないのでこれでも理解できません;;
もともとopenCVのページの動的背景更新による物体検出のサンプルをもとにつくったのでそこをベースに、と思ったのですがうまく組み合わせようとするとわからなくなってしまいます。
全く新しくここでプログラムを組みなおしたほうがよろしいのでしょうか。

それと、この場合だと直接現在つないであるカメラの映像を処理する仕様になっているのですがもともと撮影してある動画を読み取り処理するのにはどうすればいいのでしょうか。

わからないことだらけですみません。よろしくお願いします。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: openCVを使った動画像の処理について教えてください。

#9

投稿記事 by beatle » 14年前

どの変数が抜き出した物体の形を表していて、どの変数が元の物体画像を表しているかは分かりますか?
プログラムの大雑把な流れをまず理解してください。サンプルプログラムは理解してこそ力が付きます。

while文の中だけを見た感じだと、
  1. 画像をキャプチャ => frame
  2. 物体の形を計算 => msk_img
  3. 物体の形を使って元画像から物体だけを抜き出す => dst_img
という流れで無限ループしていますね。
僕が言った方法は、frame, msk_imgの情報が必要ですから、少なくともその2つの計算が終わった後に配置する必要があります。
ちなみに、frontImageとかbackImageとかいうのは、relaxmaさんのプログラムで実際に使っている変数名で置き換えてね、
という意図のもと書きました。既に存在する変数なら、新たに変数を定義する必要はありません。

コード:

IplImage* frontImage; // 物体の画像 今回はframe変数を使えば良さそう
IplImage* backImage; // 背景の画像 これは元々のプログラムには見当たらないので、新規に作る
 
for (int y = 0; y < frontImage->height; ++y)
{
    for (int x = 0; x < frontImage->width; ++x)
    {
        if (frontImageの(x,y)要素が物体以外) // ここはmsk_imgの(x,y)要素の真偽を調べれば良い
        {
            frontImageの(x,y) = backImageの(x,y); // ここはbackImageからfront変数へコピーすれば良い
        }
    }
}

閉鎖

“C言語何でも質問掲示板” へ戻る