フレーム内の人数カウント

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

フレーム内の人数カウント

#1

投稿記事 by nancy » 8年前

はじめまして。大学の卒業論文での研究課題で行き詰ってます、アドバイス下さい。

現在、単眼固定カメラで動画を撮り画面内に映っている人の数をリアルタイムで数え上げるプログラムを考えています。

物体抽出は「OpenCV.サンプルコード」をベースにしてます。
(http://opencv.jp/sample/accumulation_of ... ground_sub)

その後、物体領域を座標で表し、得られた座標の数だけ物体が存在すると判断してます。

が、問題がいくつかあって困ってます。

①物体抽出自体が不完全(影が残る・ゴーストの発生・背景色によっては物体が削れる)
②物体が少しでもくっつくと物体領域が1つに結合されてしまう
③②のせいで、当然複数人が重なってしまうと人数が合わない

何か解決のヒントはないでしょうか。一応色々な論文を調べてはいるのですが、使えそうな論文に出会えません。
助けて下さい、よろしくお願いします。

コード:

#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <ctype.h>
#include <stdio.h>
#include <math.h>


#define	INIT_TIME		350
#define	Zeta			10.0	

#define	LED_THRESH		80
#define	LED_WIDTH		20
#define	GREEN_HEIGHT	80
#define	LED_SPLIT		15
#define	GREEN_SPLIT		50


int main (int argc, char **argv)
{
	
	double B_PARAM = 1.0 / 150.0;
	double T_PARAM = 1.0 / 350.0;

	int frame_count;
	int key;
	int repeat;

	int x, y, x2, y2, i, j;
	int white = 0;
	int led_width = 0;
	int start, end;
	int target_count = 0;

	int led_line[640] = {0};
	int led_start[50] = {0};
	int led_end[50] = {0};
	int led_wide[50] = {0};

	int green_height = 0;
	int obj_count = 0;
	int obj_count_new = 0;

	int green_line[480] = {0};
	int green_start[50] = {0};
	int green_end[50] = {0};



	//動画ファイル取得
	CvCapture *capture = cvCreateFileCapture( "Video.avi" );
	if(capture == NULL)
	{
		printf( "動画ファイルが見つかりませんでした。\n");
		cvWaitKey(0);
		return -1;
	}


	//入力画像のサイズ取得
	IplImage *input = cvQueryFrame( capture );
	CvSize sizeofimage = cvGetSize( input );
	int height = input->height;
	int width = input->width;

	
	
	//物体抽出に必要なデータ領域の準備
	IplImage *kido_average = cvCreateImage (sizeofimage, IPL_DEPTH_32F, 3);
	IplImage *OUTPUT = cvCreateImage (sizeofimage, IPL_DEPTH_8U, 1);
	IplImage *kido_ampli = cvCreateImage (sizeofimage, IPL_DEPTH_32F, 3);
	IplImage *kido_low = cvCreateImage (sizeofimage, IPL_DEPTH_32F, 3);
	IplImage *kido_top = cvCreateImage (sizeofimage, IPL_DEPTH_32F, 3);
	IplImage *input_save = cvCreateImage (sizeofimage, IPL_DEPTH_32F, 3);
	IplImage *kido_mask = cvCreateImage (sizeofimage, IPL_DEPTH_8U, 1);
	
	IplImage *obj = cvCreateImage (sizeofimage, IPL_DEPTH_8U, 3);
	IplImage *output = cvCreateImage( sizeofimage, IPL_DEPTH_8U, 1 );


	
	
	// 背景の輝度平均を計算する
	cvSetZero (kido_average);
	for (frame_count = 0; frame_count < INIT_TIME; frame_count++)
	{
		input = cvQueryFrame (capture);
		cvAcc (input, kido_average);
	}

	cvConvertScale (kido_average, kido_average, 1.0 / INIT_TIME);

	
	
	// 背景の輝度振幅を計算する
	cvSetZero (kido_ampli);
	for (frame_count = 0; frame_count < INIT_TIME; frame_count++) 
	{
		input = cvQueryFrame (capture);
		cvConvert (input, input_save);
		cvSub (input_save, kido_average, input_save);
		cvPow (input_save, input_save, 2.0);
		cvConvertScale (input_save, input_save, 2.0);
		cvPow (input_save, input_save, 0.5);
		cvAcc (input_save, kido_ampli);
	}

	cvConvertScale (kido_ampli, kido_ampli, 1.0 / INIT_TIME);


	//背景となりうる画素の輝度振幅の最小値を求める
	cvSub (kido_average, kido_ampli, kido_low);
	cvSubS (kido_low, cvScalarAll (Zeta), kido_low);
	
	//背景となりうる画素の輝度振幅の最大値を求める
	cvAdd (kido_average, kido_ampli, kido_top);
	cvAddS (kido_top, cvScalarAll (Zeta), kido_top);


	//ウィンドウの生成
	cvNamedWindow( "Input", CV_WINDOW_AUTOSIZE );
	cvNamedWindow( "Kido_Mask", CV_WINDOW_AUTOSIZE );
	cvNamedWindow( "Kido_Diff", CV_WINDOW_AUTOSIZE );
	//cvNamedWindow( "Obj", CV_WINDOW_AUTOSIZE );
//	cvNamedWindow( "Output", CV_WINDOW_AUTOSIZE );



	



	//動画処理のループ
	while(1)
	{
		//物体領域を表示する画面のクリア
		cvSetZero( obj );
		cvSetZero( OUTPUT );
		
		//入力画像を取得
		input = cvQueryFrame( capture );

		if( input == NULL )
		{
			break;
		}

	
		//入力画像を複製
		cvConvert( input, input_save );

		
		//入力画像(複製)の背景と判断された部分を黒く塗りつぶす
		cvInRange (input_save, kido_low, kido_top, kido_mask);
		cvNot( kido_mask, kido_mask );

		

//////////////////////////背景更新///////////////////////////////////////////////////////////////	
//		//輝度振幅を再計算する
//		cvSub (input_save, kido_average, input_save);
//		cvPow (input_save, input_save, 2.0);
//		cvConvertScale (input_save, input_save, 2.0);
//		cvPow (input_save, input_save, 0.5);
//
//		// 背景と判断された領域の背景の輝度平均と輝度振幅を更新する
//		cvRunningAvg (input, kido_average, B_PARAM, kido_mask);
//		cvRunningAvg (input_save, kido_ampli, B_PARAM, kido_mask);
//
//		// 物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
//		cvNot (kido_mask, kido_mask);
//		cvRunningAvg (input_save, kido_ampli, T_PARAM, kido_mask);
////////////////////////背景更新////////////////////////////////////////////////////////////////



		////膨張
		//for( repeat=0; repeat<2; repeat++ )
		//{
		//	cvDilate( kido_mask, kido_mask, NULL, 1);
		//}

		//人物の幅を描画(赤)
		for( x=0; x<width-1; x++ )
		{
			for( y=0; y<height-1; y++ )
			{
				if( kido_mask->imageData[kido_mask->widthStep*y+x] != 0 )
				{
					white++;
				}
			}
			if( white > LED_THRESH )
			{
				led_line[x] = 1;
			}
			else
			{
				led_line[x] = 0;
			}
			white = 0;
		}

		i = 0;
		target_count = 0;
		for( x=0; x<640; x++ )
		{
			if( led_line[x] == 1 )
			{
				start = x;

				while( led_line[x] == 1 )
				{
					x++;
					led_width++;
				}

				end = x;

				if( led_width < LED_WIDTH )
				{
					for( x2=start; x2<end; x2++ )
					{
						led_line[x2] = 0;
					}
				}
				else
				{
					led_start[i] = start;
					led_end[i] = end;
					led_wide[i] = led_width;

					i++;
					target_count++;
				}
			}
			led_width = 0;
		}

		//物体が縦に分割されたらくっつける
		for( i=0; i<target_count; i++ )
		{
			if( led_start[i+1]-led_end[i] < LED_SPLIT )
			{
				if( led_wide[i] < LED_SPLIT || led_wide[i+1] < LED_SPLIT )
				{
					for( x=led_end[i]; x<led_start[i+1]; x++ )
					{
						led_line[x] = 1;
					}
				}
			}
		}

		//改めて物体領域のx座標を取得
		i = 0;
		target_count = 0;
		for( x=0; x<640; x++ )
		{
			if( led_line[x] == 1 )
			{
				start = x;

				while( led_line[x] == 1 )
				{
					x++;
				}

				end = x;

				led_start[i] = start;
				led_end[i] = end;
				led_wide[i] = led_width;

				cvRectangle( obj, cvPoint(led_start[i],0), cvPoint(led_end[i],height-1), CV_RGB(255,0,0), -1 );

				i++;
				target_count++;
			}
			led_width = 0;
		}


		

		//物体の高さを描画
		obj_count = 0;
		obj_count_new = 0;
		white = 0;
		for( i=0; i<target_count; i++ )
		{
			for( y=0; y<height-1; y++ )
			{
				for( x=led_start[i]; x<led_end[i]; x++ )
				{
					if( kido_mask->imageData[kido_mask->widthStep*y+x] != 0 )
					{
						white++;
					}

					if( white > LED_WIDTH-1 )
					{
						green_line[y] = 1;
					}
					else
					{
						green_line[y] = 0;
					}

				}
				white = 0;
			}

			j = 0;
			for( y=0; y<480; y++ )
			{
				if( green_line[y] == 1 )
				{
					start = y;

					while( green_line[y] == 1 )
					{
						y++;
					}

					end = y;

					
					green_start[j] = start;
					green_end[j] = end;

					j++;
					obj_count++;
				}
				green_height = 0;
			}

			//物体が横に分割したらくっつける
			for( j=0; j<obj_count; j++ )
			{
				if( green_start[j+1]-green_end[j] < GREEN_SPLIT )
				{
					for( y=green_end[j]; y<green_start[j+1]; y++ )
					{
						green_line[y] = 1;
					}
				}
			}

			//改めて物体領域のy座標を取得
			for( y=0; y<480; y++ )
			{
				if( green_line[y] == 1 )
				{
					start = y;

					while( green_line[y] == 1 )
					{
						y++;
						green_height++;
					}

					end = y;

					if( green_height < GREEN_HEIGHT )
					{
						for( y2=start; y2<end; y2++ )
						{
							green_line[y2] = 0;
						}
					}
					else
					{
						green_start[j] = start;
						green_end[j] = end;

						cvRectangle( obj, cvPoint(led_start[i],green_start[j]), cvPoint(led_end[i],green_end[j]), CV_RGB(0,255,0), -1 );
						cvRectangle( input, cvPoint(led_start[i],green_start[j]), cvPoint(led_end[i],green_end[j]), CV_RGB(0,255,255), 2 );

						j++;
						obj_count_new++;
					}
				}
				green_height = 0;
			}
		}

		//結果の表示
		cvShowImage( "Input", input );
		cvShowImage( "Kido_Mask", kido_mask );
		cvShowImage( "Kido_Diff", OUTPUT );
		cvShowImage( "Obj", obj );
		cvShowImage( "Output", output );
		
		key = cvWaitKey (1);

		//[r]キーで撮影
		if(key == 0x72 )
		{
			cvSaveImage( "test_pict4.bmp", input );
		}


		//[Esc]キーで終了
		if(key == 0x1b)
		{
			break;
		}

		//[スペース]キーで一時停止
		else if(key == 0x20)
		{
			cvWaitKey(0);
		}

		//[>]キーで100フレームとばす
		else if(key == 0x3e)
		{
			for(frame_count=0; frame_count<100; frame_count++)
			{
				input = cvQueryFrame( capture );
			}
		}
				
	}

	//ウィンドウ,画像領域の破棄
	cvDestroyAllWindows( );
	cvReleaseCapture( &capture );
	cvReleaseImage (&kido_average);
	cvReleaseImage (&kido_ampli);
	cvReleaseImage (&kido_low);
	cvReleaseImage (&kido_top);
	cvReleaseImage (&input_save);
	cvReleaseImage (&kido_mask);
	cvReleaseImage (&output);
	cvReleaseImage (&obj);

	return 0;
}

nancy

Re: フレーム内の人数カウント

#2

投稿記事 by nancy » 8年前

追伸:ソースコードを

コード:

で囲むの忘れてしまいました、申し訳ありません!

アバター
あたっしゅ
記事: 334
登録日時: 10年前
住所: 東京23区
連絡を取る:

Re: フレーム内の人数カウント

#3

投稿記事 by あたっしゅ » 8年前

「大学の卒業論文」と「高専プログラムコンテスト」は違うでしょうが、

http://kabumatome.doorblog.jp/archives/65714060.html
画像処理等でサイコロの個数の算出に挑むプログラムコンテストで「人力で数えた」宇部高専が優勝(※上位6チーム中4チームが人力で数えてた) - 市況かぶ全力2階建(ja)

というのが、ありました。
手提鞄あたっしゅ、[MrAtassyu] http://ameblo.jp/mratassyu/
手提鞄屋魚有店(てさげかばんやうおありてん)
レスがついていないものを優先して、レスしています。時々、見当外れなレスをします。

AKIЯA
記事: 58
登録日時: 8年前

Re: フレーム内の人数カウント

#4

投稿記事 by AKIЯA » 8年前

面白そうなことやってますね。
すごい難しそうですね。

②物体が少しでもくっつくと物体領域が1つに結合されてしまう

普通に考えたらこれが出来るかどうかさっぱりわかりませんが
以前openCVにあった顔認識でちょっと遊んで見たことがあるのですが
サンプルソースにあった結論から言うと顔認識がまだ甘かったので誤認識とかもありましたが
(私の顔が人間離れしていたのかっという突っ込みは置いといて)
何かマーカーとなりえるもので人数を確認しないといけないのではないでしょうか?っと単純に思いました。

もしくはX-BOXのキネクトのように人間の体を判別しているはずなので、もしかしたら参考になるかもしれません。
https://sites.google.com/site/moggproject/

このサイト見るとKinect for Windows SDKで作られているはずなので
その辺も見てみるとよいかもしれません。

この記事を見る限りでは複数人認識できるようなことが書かれています。
http://www.atmarkit.co.jp/fdotnet/speci ... 02_02.html

全然違ったらごめんなさい。

nancy

Re: フレーム内の人数カウント

#5

投稿記事 by nancy » 8年前

返信してくれた皆さんありがとうございます!ただ・・・ちょっと主旨が違うみたいです(>_<)

教授曰く、複数人が重なった状態での人数カウントアルゴリズムはすでに存在しているみたいなのですが(日本かどうかは不明)、どうしてもその論文を見つけることができません。

どんな些細な情報でも構いませんので、ご協力お願いします!!

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

Re: フレーム内の人数カウント

#6

投稿記事 by beatle » 8年前

nancy さんが書きました:教授曰く、複数人が重なった状態での人数カウントアルゴリズムはすでに存在しているみたいなのですが(日本かどうかは不明)、どうしてもその論文を見つけることができません。
では先生に教えてもらえばいいのではないでしょうか.

nancy

Re: フレーム内の人数カウント

#7

投稿記事 by nancy » 8年前

聞いて教えてくれるならわざわざ皆さんに助けを求めたりしません。私は公務員への就職が決まっているのですが、研究者の道に進む気のない私を教授は快く思っていないらしく(私以外の学生は全員研究者志望です)、普段から不公平な態度をとられています。学科長などにも相談をしたのですが、大学規則に反した行動でない以上介入の余地がないそうです。とにかく卒論を完成させなければ卒業の保証はできないと。研究室の他の学生たちも教授が怖くて私に関わろうとしてくれません。なのでこうしてネット上で協力を仰ぐことしかできない状態なんです。

プログラミングと関係のない話をしてすいませんでした。それだけ切羽詰まった状態であることを分かってほしかっただけです。どうかご協力をお願いします・・・。

アバター
へにっくす
記事: 630
登録日時: 8年前
住所: 東京都

Re: フレーム内の人数カウント

#8

投稿記事 by へにっくす » 8年前

過剰に反応するのは分かりますが
おそらくここでは明確な回答は得られないかと思います。
なぜならここは大学の研究機関が関わっているわけではないからです。
#別に意地悪を言ってるわけではありませんよ。
#ただ聞く場所を間違えていると申し上げたいだけです。(つまり過度な期待はしないこと)
#だってここはおもにC言語についての質問サイトだし。

他の大学の友人とかはいないのでしょうか?
いないならば作るしかないですよ。
他に似たようなことをやってるサークルなどに連絡をとってみるとか。
研究室の中だけでは限界があるんです。
オープンソースって自分だけでは限界があるから公開するわけですよね。
それと同じです。

個人的には、あなたは公務員に就職するということで、
研究がおろそかにならないように教授もあえて厳しく接しているものとみました。
研究者ってのは他人があまり関わらない仕事ですが、
公務員となると、いろんな人と接しなければならなくなります。
なかには教授のように不公平な人がいるかもしれません。
いまここで逃げるようなことがあると、この先もっとつらいですよ?
最後に編集したユーザー へにっくす on 2012年11月04日(日) 19:13 [ 編集 1 回目 ]
written by へにっくす

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 10年前
住所: 北海道札幌市
連絡を取る:

Re: フレーム内の人数カウント

#9

投稿記事 by Dixq (管理人) » 8年前

質問文は、codeタグで書き直しておきました。

さて、私は画像処理の専門家じゃないので、詳しいことはわかりませんが、この辺で解決できませんか?
https://www.google.co.jp/search?oq=open ... C%E5%87%BA

リンク先を見るに、物体が重なっていても人物として検出出来ていそうです。
OpenCVの機械学習機能を使って、人物の形を学習させるって手もあるかもしれません。
OpenCVには便利な機能がたくさんありますが、組み合わせて使う物が多いです。
そして、知らなければどう組み合わせたらやりたいことが実現できるかということが思いつかないでしょう。
なので、一度
CVの機能を端から全部扱ったプログラムを書いてみることをオススメします。
公式リファレンスも充実していますが、本の方が読みやすいので、書店に行って気に入った本を探してみるとよいでしょう。
http://www.amazon.co.jp/s/ref=nb_sb_nos ... rds=OpenCV

教授に関しては、いろんな人がいますよね。
私が卒業した大学にもいました。
入ると過半数が卒業できないという魔の研究室で、教授自身は留年させることに喜びを感じているかのようなことをよく口にしていました。

幸運をお祈りします。

アバター
kazuoni
記事: 17
登録日時: 10年前
住所: 愛知
連絡を取る:

Re: フレーム内の人数カウント

#10

投稿記事 by kazuoni » 8年前

>①物体抽出自体が不完全(影が残る・ゴーストの発生・背景色によっては物体が削れる)
これに関してはどのような条件下のもとで実験を行うかに大きく依存すると思います。
ここを考えるのは余裕が生まれてからにして、②以降のことを考えることをお勧めします。
なので、現状は以下のようにしておけばよいかと思います。
・なるべく均一色の背景
・物体は背景色とは全く違う色を持つ物体
・影が影響しないような環境光の調節

>②物体が少しでもくっつくと物体領域が1つに結合されてしまう
ソースを見ていませんが、領域単位で物体を認識しているからこのようなことが起きているのでしょうか?
これも制約条件を付ければクリアは可能かと思います。
・物体は急激な変形・合体・分裂をしない
・物体の移動は線形である
・個々の物体はそれぞれ個性的な物体である(例えば色が全く違う、形状が違うなど)

初期フレームで(重なっていない)物体を抽出し、
それぞれに対し物体の重心を求め、多角形(長方形)や楕円などで近似します。
次フレームからは前数フレームの移動量(加速度)を使って近似した図形の線形追跡を行います。
このとき重なると線形追跡が不安定になりますが、個々の物体は個性的であるので、
物体を識別させれば背面に隠れた場合には「止まっている」として、加速度=0とすればよいと思います。
また、これに加えて「画面領域外に(物体領域の何%)出たかどうか」を判定すれば十分だと思います。

>③②のせいで、当然複数人が重なってしまうと人数が合わない
これは②を解決すればOKです。


長いので次レスに伸びます
最後に編集したユーザー kazuoni on 2012年11月05日(月) 12:51 [ 編集 1 回目 ]

アバター
kazuoni
記事: 17
登録日時: 10年前
住所: 愛知
連絡を取る:

Re: フレーム内の人数カウント

#11

投稿記事 by kazuoni » 8年前

さて、ここまで比較的単純な方法を提示てきましたが、ここからが重要です。
(0、これらが教授の意図したアルゴリズムであるのかどうなのか)
1、自分のベースにする先行研究は何か
2、先行研究で何が問題なのか
3、自分のテーマは何なのか
4、自分の創意工夫点はどこなのか
です。

今回のnancyさんの質問を見るからでは「とりあえずOpenCVにあったサンプルをいじってうまくいきませんか?」と伝わります。
それでは論文を執筆できません。
一番大切なのは、「既存手法の問題点を挙げ、その問題点を解決するアプローチ」です。
まずは先行研究を決めましょう。
google scholar、CiNiiなどで物体認識をテーマに扱っているもの(かつなるべく新しいもの)をピックアップし、隅々まで読んでください。
(中部大学が歩いている人の認識をやっていた気がします)
論文ごとに長所、短所があります。そのなかで扱われていない短所を問題点として定義しましょう。
>何か解決のヒントはないでしょうか。一応色々な論文を調べてはいるのですが、使えそうな論文に出会えません。
と書かれていますが、これはマイナスではなくプラスに考えてください。
(調査が不十分かもしれないが)まだ未解決の分野があるということです。

ここまで決まればあとは実装、実験だけです。


公務員試験が並行して研究に時間がさけず、大変だったのだと察します。
逆に言えば、コツコツ進めてこなかったツケです。
時間がないのかもしれませんが、目の前だけの模索ではなく全体を見渡して、
再度計画を立てて研究を進めていってください。
細かいテーマさえ決まってしまえば、さくさく進むと思います。

がんばってください(私もがんばります・・・)

nancy

Re: フレーム内の人数カウント

#12

投稿記事 by nancy » 8年前

返信してくださった皆さんありがとうございます。特に2レスも使って丁寧に考えて下さったkazuoniさんには本当に感謝です。

やはり趣味の範囲でプログラミングするのと、論文のための「研究」では全くアプローチが違うのですね。

kazuoniさんのアドバイスを参考に、従来手法の問題点等に着目して引き続き論文調べを頑張ってみたいと思います。

ここの方々は皆親切でいい人ばかりですね(^-^)

本当にどうもありがとうございました☆

閉鎖

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