JPEGを読む(6) フレームヘッダとスキャンヘッダ
Interpret frame header
Figure E.7の"Interpret frame header"の部分Decoder#readFrameHeader
を作る。ビットフィールドの定義はTable B.2にあって、それぞれのフィールドの意味はこう。
- Lf: フレームヘッダの長さ
- P: サンプル精度。フレーム内のコンポーネントのサンプルのビットの精度らしい。コンポーネントもサンプルも精度もわからない。Annex Aに書いてあるらしいけどまだ読んでない。
- Y, X: 画像のサンプルのライン数と列数。要するに縦横の画素数のこと。
- Nf: フレーム内のコンポーネントの数
- Csi: i番目のコンポーネントのIDらしい
- Hi, Vi: i番目のコンポーネントの水平サンプリング係数と垂直サンプリング係数らしい。これもAnnex Aで説明されている。
- Tqi: 量子化表のどれを使うか。ハフマン符号表にもあったThみたいなやつが量子化表にもあって、どの表を使うかをこの値で示している。
これは順に読めばいいだけなのでコードは長いけど簡単。出力のためにDecoder
構造体にも値を追加する。
type componentParam struct { c, h, v, tq uint8 } func (p *componentParam) String() string { return fmt.Sprintf("(C=%d H=%d V=%d Tq=%d)", p.c, p.h, p.v, p.tq) } type frameHeader struct { marker uint8 p uint8 y, x uint16 params []*componentParam } func (h *frameHeader) String() string { return fmt.Sprintf("p=%d (x, y)=(%d, %d) params=%v", h.p, h.x, h.y, h.params) } type Decoder struct { // (略) frameHeader *frameHeader } // (略) func (d *Decoder) readFrameHeader() (*frameHeader, error) { m, err := d.readMarker() if err != nil { return nil, err } if !m.isFrameMarker() { return nil, ErrUnexpectedMarker } lf, err := d.readUint16() if err != nil { return nil, err } p, err := d.readUint8() if err != nil { return nil, err } y, err := d.readUint16() if err != nil { return nil, err } x, err := d.readUint16() if err != nil { return nil, err } nf, err := d.readUint8() if err != nil { return nil, err } var componentParams []*componentParam for i := 0; i < int(nf); i++ { c, err := d.readUint8() if err != nil { return nil, err } hv, err := d.readUint8() if err != nil { return nil, err } h := hv >> 4 v := hv & 0xF tq, err := d.readUint8() if err != nil { return nil, err } componentParams = append(componentParams, &componentParam{ c: c, h: h, v: v, tq: tq, }) } ret := &frameHeader{ p: p, y: y, x: x, params: componentParams, } return ret, nil } // (略) func (d *Decoder) decodeFrame() error { header, err := d.readFrameHeader() if err != nil { return err } d.frameHeader = header ...
これで適当な画像を読み込むとこのような表ができる。
Marker | Lf | P | X | Y | Nf |
---|---|---|---|---|---|
0xFFC0 | 17 | 8 | 400 | 400 | 3 |
Nf=3なのでコンポーネントパラメータは3つある。
Csi | Hi | Vi | Tqi |
---|---|---|---|
1 | 2 | 2 | 0 |
2 | 1 | 1 | 1 |
3 | 1 | 1 | 1 |
Decode scan
スキャンをデコードする。スキャンというのは、実際に符号化されたデータが格納されたセグメントのことらしい。
図の通り、スキャンヘッダーを読んで、あとはinterval
の個数だけ繰り返しペイロードを読む。中身や個数は置いておいて、とりあえずスキャンヘッダを読む。定義はFigure B.3, Table B.4にある。
- Ls: スキャンヘッダの長さ
- Ns: スキャン内のコンポーネントの数。
- Csj: コンポーネントのセレクター。フレームヘッダのCiと一致して、以降のTdjやTajがどのコンポーネントに適用されるかを示す。ひとつのスキャンに対応するコンポーネントのパラメータには制約があるみたいだけど一旦置いておく。
- Tdj, Taj: このスキャンのデータを、どのエントロピー符号化表で復号するかを指定する識別子。エントロピー符号化表というのは算術符号化表もしくはハフマン符号化表のことで、TdjはDC用、TajはAC用。ここで複数あったハフマン符号化表のどれを使うかを指定している。ちなみに算術符号orハフマン符号はフレームヘッダで指定される。
- Ss, Se: DCTの場合は1つの要素に含まれるDCT係数の配列の最初と最後の位置。1つの要素は8x8で64個あるので、それぞれSs=0, *Se"=63で固定となる。階層型DCTの時や可逆変換の時は要素の単位が変わってくるので意味のある値になるらしいけどこれも一旦置いておく。
- Al, Ah: これらも階層型DCTや可逆変換の時以外は0なので保留。移動小数点を規定しているらしい。
あとはこれを読むだけ。コードはフレームの時とほとんど同じで読むだけなので省略。読むと以下のような表ができる。
Ls | Ns | Ss | Se | Ah | Al |
---|---|---|---|---|---|
12 | 3 | 0 | 63 | 0 | 0 |
ここでもNs=3なのでスキャンコンポーネントのパラメータは3つ。こういう感じになっている。
Csi | Tdi | Tai |
---|---|---|
1 | 0 | 0 |
2 | 1 | 1 |
3 | 1 | 1 |
まだわかってないこと
続いて、スキャンの中身を読み始める前にそろそろ全体的な構造を把握しないとわからないことが増えてきた。
このあたりがわかっていないのでこのまま読み進めることができない。なのでAnnex Aに戻って定義を読み直す。
その前に、一旦はペイロードの内容を全部読み飛ばしてファイルを最後まで読めるようにしてもいい。こんな感じで。
func (d *Decoder) decodeResetInterval() error { for { _, err := d.readByte() if err != nil { return err } } }
これをつなげて動けば一応、読み取ったヘッダなどを出力したりして遊ぶことはできる。