井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

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

井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#1

投稿記事 by 間流 » 10年前

opencvで画像処理ソフトを作成しています
井村先生のラベリングクラスを使って、面積が7以上の領域の情報を得ようとしています
しかし実際にプログラムを動かしてみると、明らかに情報を得られた領域が少ないのです
(画像中の面積7以上の領域を実際に数えてみると100個以上あるのに、10個ぐらいしか情報が得られていない)

プログラムの何処がおかしいのか教えていただけるでしょうか?

以下、関係のありそうな部分を残して、一部省略したソースです

コード:

int main (int argc, char **argv){
	int line = 0;
	short *dst;
	LabelingBS labeling;
	RegionInfoBS	*ri;
	ri = labeling.GetResultRegionInfo( 0 );	
	IplImage *src_img, *dst_img, *dst_img2;
	IplImage *tmp_img;
	// 画像の読み込み
	if (argc != 2 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0)
		return -1;
	tmp_img = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_16S, 1);
	dst_img = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_8U, 1);
	// Cannyによるエッジ画像の作成
	cvCanny (src_img, dst_img, 50.0, 200.0);
            // 出力用画像領域の確保を行なう
	dst_img2 = cvCreateImage (cvSize (dst_img->width * 2, dst_img->height), dst_img->depth, dst_img->nChannels);
             // 画像のサイズ変更を行う
        cvResize (dst_img, dst_img2,CV_INTER_AREA);
	//dst_img2のx位置2nを黒にする
	for(line = 0; line <= dst_img2->width ; line++ ){
	cvLine(dst_img2,cvPoint(2*line,0),cvPoint(2*line, dst_img->height),CV_RGB(0,0,0),1,4,0);
	}

	//ラベリング
	dst = new short[ dst_img2->width * dst_img2->height ];
	labeling.Exec((uchar*)dst_img2, dst ,dst_img2->height,dst_img2->width,true,6);

	//ラベリングの領域情報
	for(int i = 0;i < labeling.GetNumOfResultRegions(); i++){
		RegionInfoBS    *ri = labeling.GetResultRegionInfo( i );
		cout << "-------------------------------" << endl;
		cout << "領域ナンバー:" << ri->GetResult() << endl;
		cout << "画素数:" << ri->GetNumOfPixels() << endl;
	}
		cout << "-------------------------------" << endl;
		cout << "領域の総数:" << labeling.GetNumOfRegions() << endl;
		cout << "面積が指定の画素以上の領域数:" << labeling.GetNumOfResultRegions() << endl;


アバター
usao
記事: 1887
登録日時: 11年前

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#2

投稿記事 by usao » 10年前

あなたが使われている
>井村先生のラベリングクラス
とかいうものの仕様を説明しないと答えようがない気がしますが.
(ラベリング処理の仕様というか対象は何なのか.例えば
 位置的に連結していて且つ同値な画素で領域を作るのか
 それとも 非0の画素であれば値が異なっていても連結位置であれば同領域とするのか,みたいな)

あと,それ以前に,line27のキャストが非常に気になるのですが,これがバグだというオチではありませんよね?
(LabelingBSクラスの仕様次第ですからあってるのかどうかわかりませんけど,
 IplImage*をuchar*にキャストして渡すのが自然な行為とは到底思えない)

間流

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#3

投稿記事 by 間流 » 10年前

2値画像を8連結、同値の画素で領域を作っています
仕様を見直してみましたがline27のキャストは問題ないようです


アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#5

投稿記事 by みけCAT » 10年前

テストに使用している画像をアップロードすることはできますか?
(著作権にも気をつけてください)
https://gigafileupload.com/
http://www.axfc.net/uploader/
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1887
登録日時: 11年前

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#6

投稿記事 by usao » 10年前

>仕様を見直してみましたがline27のキャストは問題ないようです

ざっと検索してみましたが,ここ↓のやつでしょうか? だとすれば,どう見ても違うとしか思えません.
http://oshiro.bpe.es.osaka-u.ac.jp/peop ... s/labeling
もしそのキャストが本当に合っているのだとすれば,そのクラスは

 少なくともIplImage構造体とヘッダ部のサイズや画素情報のあるメモリ位置へのアクセス手段といった点で 互換性のあるデータ

を要求していることになります.
IplImageの画素情報がどこにどうやって存在しているのかを理解されていますか?

また↑のリンク先の図を見た限りだと,パディング(ビットマップの画素データ並びの各列を4byte境界に揃えるやつ)については
意識されていないようにも見えますから,
IplImageの画素データのメモリ位置をそのまま渡すことも問題になるかもしれません.


アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#8

投稿記事 by みけCAT » 10年前

OpenCVの画像の構造体のポインタをlabeling.Execに渡すのではなく、
画像データ本体へのポインタを渡すべきである気がします。
念のため、指定された形式のデータを自分で作って渡したほうがいいかもしれません。(ライブラリの内部構造への依存を減らす)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#9

投稿記事 by みけCAT » 10年前

Execに渡される画像データを確認するための関数を作りました。
意図したとおりのデータが渡されているか確認してください。
ExecTestの最初の引数にLabelingBSクラスのオブジェクトを指定し、
第二引数以降に本来lbs.Execに渡す引数を指定してください。
与えられた画像がカレントディレクトリのexec_test.bmpに保存されます。
例: labeling.Exec( src, result, w, h, true, 0 ); → ExecTest(labeling, src, result, w, h, true, 0 );

コード:

#include <cstdio>

void write4byte(unsigned char* out,unsigned int val) {
	out[0]=val&0xFF;
	out[1]=(val>>8)&0xFF;
	out[2]=(val>>16)&0xFF;
	out[3]=(val>>24)&0xFF;
}

void write2byte(unsigned char* out,unsigned short val) {
	out[0]=val&0xFF;
	out[1]=(val>>8)&0xFF;
}

// 与えられた画像をexec_input.bmpに保存する
int ExecTest(LabelingBS& lbs,unsigned char *target,short *result,
		int target_width,int target_height,
		const bool is_sort_region,const int region_size_min) {
	unsigned char* buffer;
	int bmpWidth=(target_width+4-1)/4*4;
	int bmpSize=0x36+4*256+bmpWidth*target_height;
	buffer=new unsigned char[bmpSize];
	buffer[0]=0x42;buffer[1]=0x4D;
	write4byte(&buffer[2],bmpSize);
	write2byte(&buffer[6],0);
	write2byte(&buffer[8],0);
	write4byte(&buffer[0xA],0x436);
	write4byte(&buffer[0xE],0x28);
	write4byte(&buffer[0x12],target_width);
	write4byte(&buffer[0x16],target_height);
	write2byte(&buffer[0x1A],0x1);
	write2byte(&buffer[0x1C],0x8);
	write4byte(&buffer[0x1E],0);
	write4byte(&buffer[0x22],bmpSize-0x436);
	write4byte(&buffer[0x26],0);
	write4byte(&buffer[0x2A],0);
	write4byte(&buffer[0x2E],0);
	write4byte(&buffer[0x32],0);
	for(int i=0;i<256;i++) {
		buffer[0x36+i*4]=i;
		buffer[0x36+i*4+1]=i;
		buffer[0x36+i*4+2]=i;
		buffer[0x36+i*4+3]=0;
	}
	for(int y=0;y<target_height;y++) {
		for(int x=0;x<target_width;x++) {
			buffer[0x436+y*bmpWidth+x]=target[y*target_width+x];
		}
		for(int x=target_width;x<bmpWidth;x++) {
			buffer[0x436+y*bmpWidth+x]=0;
		}
	}
	FILE* fp=fopen("exec_input.bmp","wb");
	if(fp!=NULL) {
		fwrite(buffer,bmpSize,1,fp);
		fclose(fp);
	}
	delete[] buffer;
	return lbs.Exec(target,result,target_width,target_height,
		is_sort_region,region_size_min);
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1887
登録日時: 11年前

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#10

投稿記事 by usao » 10年前

うーん,反応が無いので迷うけど,一応答えのようなものを書いておきますか.

(1)IplImage構造体の内容くらい確認しよう → OpenCVのリファレンスくらい見ようね.
(2)使っているラベリングクラスの仕様くらい確認しよう → 画像データ以外にも,ひょっとしてwidthとheightが逆になったりしてませんか?それ.

この2行で十分な気もしますが,(1)について詳述しておきます.

・画素データの領域はIplImageのメンバである char *imageData の指す先にあります.
 だから IplImage型オブジェクトへのアドレスをそのまま引数として指定している時点で間違っています.
・imageDataの先にある画素データは Windowsでの(?)BMPと同じように,パディング
 (各行のデータ末尾にいくらか使わない領域を追加して行のデータバイト数をあるbyte数(例えば4byte)の倍数に揃える)
 が行われるフォーマットになっています.
 (リファレンスでは「行頭のアライメントを揃える」という表現になっていますが,同じことです)
 だから,あなたが用いる画像の横幅(x方向pixel数)がたまたまその倍数になっている場合を除いては,
 そのラベリングクラスに画像データを与えるためには一工夫いるハズです.
 →例えば他の方がおっしゃられているように,ラベリングクラス側が要求する形のデータに一旦自分で変換する等です.
 →今ざっと手元にあるOpenCV2.2のリファレンスでIplImageの項を確認した感じだと,パディング領域の値が
  どうなるのかについての言及は無いようですが,
  そのラベリングクラスが値0の画素を「ラベリングしない領域」として扱うというのであれば,
  もし「パディング領域は必ず0で埋められている」みたいな状況に持ち込めさえすれば(仕様が不明なら自分で埋めるとか),
  imageDataをそのまま引き渡すという方法もあり得ます.
  (その際はラベリング処理に指定する横幅を適切に与えればよいでしょう widthStep がこのことに役立つはずです)

間流

Re: 井村先生のラベリングクラスを使用したopencvの画像処理プログラムについて

#11

投稿記事 by 間流 » 10年前

みけCATさんのプログラムを使うときに気付きました。
usaoさんの言うとおり引数の横と縦の順番を間違えていました。
アドバイスしていただいたお二人に感謝します

閉鎖

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