ページ 11

YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月10日(月) 19:36
by binoruto
初めて投稿させて頂きました。
C言語の基本的なこと、入門サイトにのっているようなことは理解していると思っていますが
C言語での実装経験はほとんどありません。
主な実装経験はC#、C++です。
よろしくお願いします。

やりたい事
カメラのセンサーからYUV422形式で静止画データを読み込んでいます。
YUV422形式のデータをRGBに変換してポインタに一時格納
このデータをjpeg形式に落としたいと思っています。

動作環境  
 OS:Linux 
 ディストリビューション:debian etch 
 コンパイラ名 : gcc-4.1.2
 API:l4v2
 関係するライブラリ:jpeglib.h

l4v2の仕様は
http://v4l.videotechnology.com/dwg/v4l2 ... X-FMT-YUYV
になります。

以下はカメラからYUV422形式で取り込んだデータをバッファに格納してjpegに落とすところまでのソースになります。
#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) 
static void
// pは入力データを格納しているポインタ
process_image                   (const void *           p)
{
	
	int           i, j;
	unsigned char *writer;
	const unsigned char *reader;
	unsigned char b, g1, g2, r;
	unsigned char fname_jpeg[FILENAME_MAX];// FILENAME_MAXは50文字
	unsigned char fname_ppm[FILENAME_MAX];
	unsigned char ext[4+1]; // 拡張子4文字 + 終端文字
	FILE          *fp;

	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr       jerr;
	unsigned char               *line;
         
	reader = (unsigned char *)p;
	
	unsigned int height = CAPTURE_HEIGHT;  //480
	unsigned int width = CAPTURE_WIDTH;   //640
	
	int u,v,u1,rg,v1;
	
// YUV形式→RGBに変換してポインタに格納します
// 計算式は抜粋してきたものを使用しています
	for (j = height;j >= 0;j--) {
    for (i = 0; i < width; i += 2) {
 
      u = reader[1];
      v = reader[3];
      u1 = (((u - 128) << 7) +  (u - 128)) >> 6;
      rg = (((u - 128) << 1) +  (u - 128) + ((v - 128) << 2) + ((v - 128) << 1)) >> 3;  
      v1 = (((v - 128) << 1) +  (v - 128)) >> 1; 
      *writer++ = CLIP(reader[0] + u1);
      *writer++ = CLIP(reader[0] - rg);
      *writer++ = CLIP(reader[0] + v1);

      *writer++ = CLIP(reader[2] + u1);
      *writer++ = CLIP(reader[2] - rg);
      *writer++ = CLIP(reader[2] + v1);
      reader += 4;
    }
  }
		// JPEG
		if ( save_jpeg == 1 )
		{
			strcpy( ext, ".jpg" );
			// 終端文字の付加
			ext[4+1] = '\0';
			sprintf( fname_jpeg, "out%05d%s", ImgNum, ext );
			fp = fopen(fname_jpeg, "w");
			if ( fp == NULL )
			{
				//Errno_Exit("Error:	Save image file");
			}

			cinfo.err = jpeg_std_error( &jerr );
			jpeg_create_compress( &cinfo );
			jpeg_stdio_dest(&cinfo, fp);
			cinfo.image_width      = width;
			cinfo.image_height     = height;
			cinfo.input_components = 3; // color component per pixel
			cinfo.in_color_space   = JCS_RGB;
			jpeg_set_defaults( &cinfo );
			jpeg_set_quality(&cinfo, 100, TRUE); // 品質 [0-100]
			jpeg_start_compress(&cinfo, TRUE);
			line = writer;
			for (i=0;i < height; i++)
			{
				line += width * 3;
				jpeg_write_scanlines(&cinfo, &line, 1); // ←ここでエラー
			}

			jpeg_finish_compress( &cinfo );
			jpeg_destroy_compress( &cinfo );

			fclose( fp );
		}
	
	ImgNum++;
}
実行しますとSegmentation faultとなります。
jpeg_write_scanlines(&cinfo, &line, 1);
までは動作しています。
jpegデータは吐き出されますが空のデータになります。

Segmentation faultについて調べましたがアクセスしたアドレス範囲がおかしい時に
起こるエラーと認識しております。正しいでしょうか。

読み書きのポインタのアドレスがおかしいと思うのですが、この場合は
実際に読んできたデータ数より大きい値のアドレスにアクセスしてそれを書き出そうとしているから
エラーが発生するのでしょうか。

その場合はどのように修正したらよろしいでしょうか。

以上です。どうぞご教授よろしくお願いします。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月10日(月) 20:19
by Justy
[color=#d0d0ff" face="monospace]
for (i=0;i < height; i++)
{
line += width * 3;
jpeg_write_scanlines(&cinfo, &line, 1);
}
[/color]

の部分って
[color=#d0d0ff" face="monospace]
for (i=0;i < height; i++)
{
jpeg_write_scanlines(&cinfo, &line, 1);
line += width * 3;
}
[/color]

だったりしませんか?

 というか、
[color=#d0d0ff" face="monospace]
jpeg_write_scanlines(&cinfo, &line, height);
[/color]

で良かったりしません?

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月10日(月) 20:28
by Justy
 追加。

[color=#d0b0c0" face="monospace]
>アクセスしたアドレス範囲がおかしい時に起こるエラーと認識しております。正しいでしょうか
[/color]

 そうです。
 アクセスが許可されていないメモリを参照すると起こります。

[color=#d0b0c0" face="monospace]
>読み書きのポインタのアドレスがおかしいと思うのですが
[/color]

 何回目の jpeg_write_scanlinesでエラーになっているのかを調べるて
その時の writerと line変数の状態を調べれば何かつかめるかもしれません。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月11日(火) 17:55
by binoruto
Justyさんありがとうございます。

>何回目の jpeg_write_scanlinesでエラーになっているのかを調べるて
>その時の writerと line変数の状態を調べれば何かつかめるかもしれません。

を参考に変数の値を出力し、デバッグしてみました。
height0でエラーが発生したため領域が確保されてないんではないかと思い、調べた所案の定
メモリ領域が確保されていませんでした。
(unsigned char *)mallocでwriterの領域を確保することでSegmentation fault
は解消されました。ありがとうございます。
for (i=0;i < height; i++) 
{ 
line += width * 3; 
jpeg_write_scanlines(&cinfo, &line, 1); 
}
もおかしいですね。言われてみてきづきました。
現在は下のようにしています。
for (i=0;i < height; i++) 
{ 
     
jpeg_write_scanlines(&cinfo, &line, 1); 
line += width * 3; 
}

新たな問題が発生しましたので、続けて投稿させて頂きます。
jpgファイルは落とせましたが、メモリ読み書き失敗しているようです。
画像データが取得できず空状態です。そこでソースを修正してみました。
するとまたエラーが発生しました。詳しくは以下で説明します。


以下はYUV422形式で入力されてきたデータをRGBに変換し格納する所です。
int y1,y2,u,v,cc;

for (j = height;j >= 0;j--) {
for (i = 0; i < width; i += 2) {

y1 = reader[cc++];
u  = reader[cc++];
y2 = reader[cc++];
v  = reader[cc++];
 		
*writer++ = CLIP(y1 + (1.402 * v));
*writer++ = CLIP(y1 - (0.344 * u) - (0.714 * v));
*writer++ = CLIP(y1 + (1.772 * u));

*writer++ = CLIP(y2 + (1.402 * v));
*writer++ = CLIP(y2 - (0.344 * u) - (0.714 * v));
*writer++ = CLIP(y2 + (1.772 * u));

}
}
このように計算式を修正したのですが、
for (i=0;i < height; i++) 
{ 
     
jpeg_write_scanlines(&cinfo, &line, 1); 
line += width * 3; 
}
またSegmentation faultエラーが発生してしまいます。
デバッグした所i=2でエラーが発生します。

writer = (unsigned char *)malloc(width * height * 3);
でwriterの領域は640*480*3をとっています。

上記計算式内で取得されると考えられるbyte数を満たしていると思うのですが
どのような原因でSegmentation faultが発生すると考えられるでしょうか。

以上です、宜しくご教授お願いします。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月11日(火) 19:46
by Justy
[color=#d0b0c0" face="monospace]
>またSegmentation faultエラーが発生してしまいます
[/color]

 んーと、

・ for (j = height;j >= 0;j--)
 これおかしくないですか?
 height+1回ループが回ることになるんですが。

・ writer
 readerから writerに書き込むところで、writerのポインタを ++していますよね?
 その状態で line = writerをしていたりしませんか?
 もしそうだとすると、この代入時点で writerは mallocしたときのポインタ値とは異なるので
RGB画像の先頭を指していないと思います。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月12日(水) 08:54
by binoruto
>  height+1回ループが回ることになるんですが。

確かにその通りです。

> ・ writer
>  readerから writerに書き込むところで、writerのポインタを ++していますよね?
>  その状態で line = writerをしていたりしませんか?
>  もしそうだとすると、この代入時点で writerは mallocしたときのポインタ値とは異なるので
> RGB画像の先頭を指していないと思います。

これはwriterのポインタを ++した時点でポインタの現在の番地がそこになるということでしょうか?
私はそれをlineに代入しているということでしょうか?

以上です、ご教授宜しくお願いします。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月12日(水) 09:37
by binoruto
>これはwriterのポインタを ++した時点でポインタの現在の番地がそこになるということでしょうか?
>私はそれをlineに代入しているということでしょうか?

writerのポインタ++する前にダミーの領域dummyを作り、++した後(for文を抜けた後)に
writer=dummy;

とすることで先頭番地に移動することができました。

考え方は間違っているでしょうか?ソースは以下のように修正しました。

以上です、宜しくお願いします。
writer = (unsigned char *)malloc(width * height * 3);
unsigned char * dummy = writer;
int y1,y2,u,v,cc;
	
cc=0;

//for (j = height;j >= 0;j--) {
for (j = 0;j < height;j++) {
	for (i = 0; i < width; i += 2) {
	 	
	y1 = reader[cc++];
u = reader[cc++];[/pre]
y2 = reader[cc++];[/pre]
v = reader[cc++];[/pre]
*writer++ = CLIP(y1 + (1.402 * v));
*writer++ = CLIP(y1 - (0.344 * u) - (0.714 * v));
*writer++ = CLIP(y1 + (1.772 * u));
*writer++ = CLIP(y2 + (1.402 * v));
*writer++ = CLIP(y2 - (0.344 * u) - (0.714 * v));
*writer++ = CLIP(y2 + (1.772 * u));
}
}
writer = dummy;
[/pre]

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月12日(水) 12:42
by Justy
[color=#d0b0c0" face="monospace]
>これはwriterのポインタを ++した時点でポインタの現在の番地がそこになるということでしょうか?
>私はそれをlineに代入しているということでしょうか?
[/color]

 そう見えます。

 あとよくみたら、
[color=#d0d0ff" face="monospace]
jpeg_write_scanlines(&cinfo, &line, 1);
[/color]

も変ですね。
 
 lineは既にポインタなので、&は要らないと思います。



[color=#d0b0c0" face="monospace]
>とすることで先頭番地に移動することができました
[/color]

 それでも問題はないですが、mallocしたときのアドレスは変化させないで、
別の変数の方を ++していった方が自然な感じがします。

[color=#d0d0ff" face="monospace]
unsigned char *pRGBImageTop, *p;
pRGBImageTop = (unsigned char *)malloc(width * height * 3);
p = pRGBImageTop;
// ...
for (j = 0;j < height;j++) {
for (i = 0; i < width; i += 2) {
// ...
*p++ = CLIP(reader[0] + u1);
// ...
}
}
// ...
jpeg_start_compress(&cinfo, TRUE);
p = pRGBImageTop;
for(i = 0; i < height; i++)
{
jpeg_write_scanlines(&cinfo, p, 1);
p += width * 3;
}
[/color]

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月12日(水) 12:55
by binoruto

> [color=#d0d0ff" face="monospace]
> jpeg_write_scanlines(&cinfo, &line, 1);
> [/color]

> も変ですね。
>  
>  lineは既にポインタなので、&は要らないと思います。
>
これは一度試してみます。
>
>
> [color=#d0b0c0" face="monospace]
> >とすることで先頭番地に移動することができました
> [/color]

>  それでも問題はないですが、mallocしたときのアドレスは変化させないで、
> 別の変数の方を ++していった方が自然な感じがします。
>
> [color=#d0d0ff" face="monospace]
> unsigned char *pRGBImageTop, *p;
> pRGBImageTop = (unsigned char *)malloc(width * height * 3);
> p = pRGBImageTop;
> // ...
> for (j = 0;j < height;j++) {
> for (i = 0; i < width; i += 2) {
> // ...
> *p++ = CLIP(reader[0] + u1);
> // ...
> }
> }
> // ...
> jpeg_start_compress(&cinfo, TRUE);
> p = pRGBImageTop;

なるほど自然な感じがしますね。
もう一度ポインタについて調査してコードを整理してみます。
いろいろとありがとうございました。

Re:YUV422形式のデータからJPGファイルを取得します

Posted: 2009年8月12日(水) 13:17
by Justy
 あと、本題とは関係ない細かな点ですが。

・ mallocに対する freeがないように見える(省略されているだけかもしれませんが)

・ 文字列を扱うなら unsigned charではなく charで