ノート
訪問数 2164   最終更新 2009-06-09 (火) 10:39:33

粒子法プログラム 〜 塚越先生の本を出発点にして 〜 その2

出力ファイルmps.profを表示したい。 Flashアニメにして端末側に送信するとしたら。

OpenGLプログラミング

OpenGLで描画すればよいか?

最後に、生成した画像を「取り出す」方法。取り出してファイル化するなどが必要なため。

基本は、glReadBuffer()関数を使う。
http://opengl.jp/oglfaq/miscellaneous.htm
http://sky.geocities.jp/freakish_osprey/opengl/opengl_capture.htm

取り出した結果を所定のファイル形式に変換するためのライブラリがたくさんある。
http://opengl.jp/oglfaq/miscellaneous.htm
が、今欲しい形(MJPEGのyuv4mpegpipe方式)にはならないだろう。自分でプログラムを書くしかなさそうだ。

RGB ⇒ YUV ⇒ Flash

yuv4mpegpipeとyuv4mpegは同じか?もし同じなら、フォーマットの記述はlinux man yuv4mpegにある。以下の通り。

GRAMMAR
       The precise description of the the YUV4MPEG2 stream format is as follows:
       STREAM consists of
       -  one STREAM-HEADER
       -  unlimited number of FRAMEs
       STREAM-HEADER consists of
       -  magic string "YUV4MPEG2"
       -  unlimited number of TAGGED-FIELDs, each  preceeded  by  a  ’ ’  (single
          space) separator
       -  single ’\n’ line terminator
       FRAME consists of
       -  one FRAME-HEADER
       -  "length"  octets  of  planar  YCbCr  4:2:0 image data (If the stream is
          interlaced, then the two fields per frame are interleaved, with  proper
          spatial ordering.)
       FRAME-HEADER consists of
       -  magic string "FRAME"
       -  unlimited  number  of  TAGGED-FIELDs,  each  preceeded by a ’ ’ (single
         space) separator
       -  single ’\n’ line terminator
       TAGGED-FIELD consists of
       -  single ASCII character tag
       -  VALUE (which does not contain whitespace)
       VALUE consists of
       -  RATIO, or
       -  integer (base 10 ASCII representation), or
       -  single ascii character, or
       -  string (multiple ASCII characters)

       RATIO consists of
       -  numerator (base 10 ASCII integer)
       -  ’:’ (a colon)
       -  denominator (base 10 ASCII integer)
       Header tags fall into  three  categories:   optional,  required  and  has-
       default.   Optional tags are completely optional and may be omitted from a
       header.  Required tags must be present in a header.  Has-Default tags have
       a  default  value which is implied if the tag is not present.  Independent
       of these categories, some tags allow an "unknown" value and some do not.
       The supported tags for the STREAM-HEADER:
              W[integer] - frame width in pixels, must be > 0 (required)
              H[integer] - frame height in pixels, must be > 0 (required)
              C[string] - chroma subsampling, image data format (has default)
                      420jpeg  - 4:2:0 with JPEG/MPEG-1 siting (default)
                      420mpeg2 - 4:2:0 with MPEG-2 siting
                      420paldv - 4:2:0 with PAL-DV siting
                      411      - 4:1:1, cosited
                      422      - 4:2:2, cosited
                      444      - 4:4:4 (no subsampling)
                      444alpha - 4:4:4 with an alpha channel
                      mono     - luma (Y’) plane only
              I[char] - interlacing specification:  (has default)
                      ? - unknown (default)
                      p - progressive/none
                      t - top-field-first
                      b - bottom-field-first
                      m - mixed-mode: refer to ’I’ tag in frame header
              F[ratio] - frame-rate (has default of 0:0 == unknown)
              A[ratio] - sample aspect ratio (has default of 0:0 == unknown)
              X[string] - ’metadata’ (optional; unparsed, but passed around)
       The currently supported tags for the FRAME-HEADER:
              I[string] - framing and sampling  (required  if-and-only-if  Im  is
                     present in stream header).  Value is a string of three char-
                     acters "xyz" which have the following meanings:
                      x: frame presentation
                         t - top-field-first
                         T - top-field-first and repeat
                         b - bottom-field-first
                         B - bottom-field-first and repeat
                         1 - single progressive frame
                         2 - double progressive frame (repeat)
                         3 - triple progressive frame (repeat)
                      y: frame temporal sampling
                         p - progressive (fields sampled at same time)
                         i - interlaced (fields sampled at different times)
                      z: frame chroma-subsampling
                         p - progressive (subsampling over whole frame)
                         i - interlaced (each field subsampled independently)
                         ? - unknown (allowed only for non-4:2:0 subsampling)
              X[string] - ’metadata’ (optional; unparsed, but passed around)
       Note that a filter application must faithfully forward all "X"  tags  from
       input  pipe  to output pipe (unless it uses one of those tags, of course).
       The  supplied  library  will  do  this  automatically  if  the   functions
       y4m_copy_stream_info() and y4m_copy_frame_info() are used appropriately.

NOTES ON IMAGE DATA
       All  image  data is in the CCIR-601 Y’CbCr colorspace, presented plane-by-
       plane in row-major order.  Each sample within  each  plane  is  one  octet
       (8-bits)  in  size.   When all planes are present, they are transmitted in
       the order Y’, Cb, Cr, potentially followed by an  alpha/transparency  mask
       plane (for the 444alpha chroma format).  The alpha channel data is follows
       the same range as the Y’ luma channel:  full transparency  is  at  16  and
      full opacity is at 235.
       All  Y’  and alpha planes consist of (height X width) octets.  The size of
       the chroma planes depends on the subsampling mode:
       -  4:4:4 - (height X width) octets
       -  4:2:2 - (height X width) / 2 octets
       -  4:1:1 - (height X width) / 4 octets
       -  4:2:0 - (height X width) / 4 octets
NOTES ON FRAMING
       (More to come here.)
SEE ALSO
       mjpegtools(1), yuv4mpeg.h

yuv4mpegのファイルの例(mydumpで16進表示)

[yamanouc@oregano MJPEGtools]$ mydump WS.yuv | head -n 50
000000: 5955 5634 4d50 4547 3220 5734 3030 2048   YUV4MPEG2 W400 H
000010: 3234 3020 4631 303a 3120 4970 2041 313a   240 F10:1 Ip A1:
000020: 3120 4334 3230 6a70 6567 0a46 5241 4d45   1 C420jpeg FRAME
000030: 0a95 9697 9593 8f8c 8b8a 8f91 8983 868f
000040: 969b 9c9d 9fa0 a0a1 a19f 9e9f a0a1 a1a1
000050: a0a1 a2a3 a4a5 a6a6 a6a6 a5a4 a2a2 a2a3
000060: a3a1 a09f a0a0 a1a0 9e9f 9fa0 a1a3 a5a4
000070: a3a5 a2a0 9fa0 a1a1 a1a2 a19f 9fa0 a1a1
000080: a1a1 a1a2 a4a4 a19e 9b98 979a 9c9a 948f
000090: 8f95 9ba0 a1a1 a0a1 a4a3 a4a4 a3a2 a1a3
0000a0: a4a4 a5a6 a6a6 a5a4 a3a0 9e9c 9a96 9593
0000b0: 9392 8f8a 8989 8a8a 8989 8585 898c 8d8f
0000c0: 9192 9392 908f 918f 8c85 8088 9699 9a9b
0000d0: 9b9b 9b9c 9e9e 9b95 9089 7f75 6b63 5d5c              ukc]\
0000e0: 5f61 6265 6c76 7e84 888f 9195 989b 9e9f   _abelv~
0000f0: a0a2 a3a4 a3a1 9f9c 9a94 8d84 7d78 7167               }xqg
000100: 605d 656b 6b6b 6e71 7379 7a7b 7b7c 7d7d   `]ekkknqsyz{{|}}
000110: 7d7f 8082 8589 8e91 9490 9295 9799 9a9c   }
000120: 9d9f a1a3 a5a5 a6a8 a8a9 aaab adae afb1
000130: b2b3 b3b3 b3b3 b3b3 b3b3 b3b4 b4b4 b5b6
000140: b6b5 b2ae aeae aeab a7a3 a2a1 9d95 8a7e                  ~
000150: 766c 6865 6667 6667 6a6b 6b6b 6b6b 6c6d   vlhefgfgjkkkkklm
000160: 6d6f 6e6d 6c6b 6c6d 6d6e 6e6f 6f70 7171   monmlklmmnnoopqq
000170: 7171 7171 7171 7172 7271 7172 7273 7374   qqqqqqqrrqqrrsst
000180: 7473 7373 7373 7373 7373 7373 7373 7373   tsssssssssssssss
000190: 7373 7373 7373 7373 7373 7373 7372 7272   sssssssssssssrrr
0001a0: 7273 7373 7373 7373 7371 7171 7171 716f   rssssssssqqqqqqo
0001b0: 6e6e 6e6e 6e6d 6d6d 6d6f 6e6e 6d6d 6d6d   nnnnnmmmmonnmmmm
0001c0: 6d94 9597 9693 8f8d 8c8b 9092 8a83 868f   m
0001d0: 969c 9c9e 9fa0 a0a0 a09f 9e9f a0a1 a1a1
0001e0: a0a1 a2a3 a4a5 a6a6 a6a6 a5a4 a2a2 a2a3
0001f0: a3a1 a09f a0a0 a1a0 9e9f 9fa0 a1a3 a5a4

017710: 8487 898e 9094 9699 9b98 9796 9695 9087
017720: 8071 6c6d 7887 9298 9b99 9895 9492 9292    qlmx
017730: 9266 6666 6768 6969 6a6b 6b6b 6b6b 6b6b    fffghiijkkkkkkk
017740: 6b69 6969 6969 6969 6969 6969 6969 6969   kiiiiiiiiiiiiiii
017750: 6967 6767 6767 6767 6768 6867 6666 6565   igggggggghhgffee

01d4d0: 706f 6f6f 6f6f 6f6f 6f6d 6d6d 6d6d 6d6d   poooooooommmmmmm
01d4e0: 6d6f 6f6f 6f6f 6f6f 6f6e 6e6e 6e6e 6e6e   moooooooonnnnnnn
01d4f0: 6e8c 8c8c 8c8c 8c8c 8c8c 8c8c 8c8c 8c8c   n
01d500: 8c8c 8c8c 8c8c 8c8c 8c8b 8b8c 8c8d 8d8d
01d510: 8e90 9090 9090 9090 908e 8e8e 8e8e 8e8e

バイト数の検算: YUV420形式について、たとえばここ、要するにピクセル当り換算で12ビット。
各ピクセルの数値や変換についてはここここ
で、バイト数は1つのキーワードFRAMEの間がデータなので、FRAMEの次の0aの次=000031から次のFRAMEの1つ前=0232b0まで。232b0-31+1=23280=144000バイト=400x240ピクセル×1.5バイト(12ビット)。
形式は、YUV420 (MPEG-1)というもので、FOURCCページのYV12というもの。
つまり、まずYプレーンが400x240ピクセルx8ビット書かれ、次にUプレーンが半分の解像度で200x120ピクセルx8ビット、Vプレーンが200x120x8、と続く形式。
データの先頭(31バイト目)からの9596 9795 938f 8c8b ... はYプレーンを横にスキャンしたものだろう。次に(23280*(2/3))+31=17700+31=17731バイト目からがUプレーンで66 66 66 67 ... と続く。更に17731+(BB86/2)=17731+5DC0=1D4F1からVプレーンで8c 8c 8c 8c ...。

フォーマットの認識が正しいことを、次のプログラムを書いて確かめる。

上記ファイルを8ビットデータとしてYUVそれぞれ所定数(幅×高さ、UとVは幅/2×高/2)読み込む。
YCbCr⇒RGB変換をする。変換式はたとえばこれを根拠に、

R = 1.164(Y-16)                 + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)

を使う。プログラム(面倒なのでパラメタはすべて決めうち)は、

#include <stdio.h>
#include <stdlib.h>
#define W  400
#define H  240
int Y[W][H];
int U[W/2][H/2];
int V[W/2][H/2];

int FixRange(double x) {
  if (x > 255.0) return 255;
  if (x < 0.0) return 0;
  return (int) x;
}

main() {
  int i, j;
  char buf[65536];
  FILE *fp, *ofp;

  fp = fopen("WS.yuv", "r");
  ofp = fopen("WS.ppm", "w");
  fprintf(ofp, "P3\n# WS.ppm\n%d %d\n%d\n", W, H, 255);
  fgets(buf, sizeof(buf), fp);  /* 1st line */
  fgets(buf, sizeof(buf), fp);  /* 2nd line */
  /* Y plane */
  for (i=0; i<H; i++) {
    fread(buf, W, 1, fp);
    for (j=0; j<W; j++) {
      Y[j][i] = (buf[j] & 0xff);
    }
  }
  /* U plane */
  for (i=0; i<(H/2); i++) {
    fread(buf, (W/2), 1, fp);
    for (j=0; j<(W/2); j++) {
      U[j][i] = (buf[j] & 0xff);
    }
  }
  /* V plane */
  for (i=0; i<(H/2); i++) {
    fread(buf, (W/2), 1, fp);
    for (j=0; j<(W/2); j++) {
      V[j][i] = (buf[j] & 0xff);
    }
  }
  /* Generate Output */
  for (i=0; i<H; i++) {
    for (j=0; j<W; j++) {
      fprintf(ofp, "%3d %3d %3d ",
         FixRange( 1.164*((double)Y[j][i] - 16) + 1.596*((double)V[j/2][i/2]-128) ), /* Red */
         FixRange( 1.164*((double)Y[j][i] - 16) - 0.813*((double)V[j/2][i/2]-128)
                  - 0.391*((double)U[j/2][i/2]-128) ), /* Green */
         FixRange( 1.164*((double)Y[j][i] - 16) + 2.018*((double)U[j/2][i/2]-128) ) ); /* Blue */
      if (j==W-1) {
        fprintf(ofp, "\n");
      } else {
        fprintf(ofp, " ");
      }
    }
  }
  close(ofp);
}

これでよいことが分かったので、次にopenGLで描いた絵をYUVに変換するプログラムを作る。テストは静止画をopenGLで表示するのがよかろう。
openGLで静止画を表示するには、ここによると、RGB各1バイトずつでpixelを表すような画面データを用意する、glDrawPixels関数で表示する、が必要。

void glDrawPixels(
	GLsizei width , GLsizei height ,
	GLenum format , GLenum type , const GLvoid *pixels   
);

は、formatをRGB (GL_RGB)、typeをたとえば符号無し1バイト(GL_UNSIGNED_BYTE)に選んでしまえば、ppm形式ファイルを読み込んでそのまま描画できるだろう。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-06-09 (火) 10:39:33 (2938d)