ハフマン符号の復号はいいけど、その後のデータを読む部分を忘れていた。(3)のRECEIVEとEXTENDについて。
JPEGを読む(3) ハフマン符号の復号準備 - yunomuのブログ
RECEIVE(SSSS)
SSSSビットを読み取る処理。自明かと思ったけどちゃんと説明されていたので一応読む。SSSS
というのは4ビットの数値というだけで特に意味があるわけではない。
1ビット読み取る関数NEXTBIT
があって、特筆することはない。コードは以下。
func (d *Decoder) receive(l int) (uint8, error) { var ret uint8 for i := 0; i < l; i++ { b, err := d.nextBit() if err != nil { return 0, err } ret = ret<<1 + uint8(b) } return ret, nil }
NEXTBIT
NEXTBIT
も細かい解説がある。
変数CNT
とB
に状態を保存してある。基本的には1バイトずつ読み取ってB
に保存しておき、そこからNEXTBIT
を呼び出される度に1ビットずつシフトして取り出していく。8回読み終えると次の1バイトを読む。読んだ1バイトがDNLマーカーだった場合はスキャンを終了する。ついでなのでDNLの読み取りも実装するとこうなる。
type Decoder struct { // ... bitMask uint8 bits uint8 numLine uint16 } import "errors" var ( EOS = errors.New("end of scan") ) func (d *Decoder) readDNL() (uint16, error) { ld, err := d.readUint16() if err != nil { return 0, err } if ld != 4 { return 0, errors.New("Unexpected NDL length") } return d.readUint16() } func (d *Decoder) nextBit() (uint16, error) { if d.bitMask == 0 { b, err := d.readUint8() if err == ErrUnexpectedMarker { d.unread() m, err := d.readMarker() if err != nil { return 0, err } if m == Marker_DNL { l, err := d.readDNL() if err != nil { return 0, err } d.numLine = l return 0, EOS } return 0, ErrUnexpectedMarker } else if err != nil { return 0, err } d.bits = b d.bitMask = 0b1000_0000 } bitMask := d.bitMask d.bitMask >>= 1 if d.bits&bitMask == 0 { return 0b0, nil } else { return 0b1, nil } }
CNT
だとピンとこなかったのでビットマスクで表現した。スキャンの終了はエラー値を定義して表現することにした。あとは以前に実装したDecoder#readUint8
とDecoder#readMarker
, Decoder#unread
で簡単に実装できる。
DNLの意味は一旦置いておく。
EXTEND(V, T)
EXTEND
は、RECEIVE
で読み取った値を符号つき整数に変換する処理となる。V
はRECEIVE
の戻り値で、T
はRECEIVE
の引数となっている。
フローチャートの通りに実装することは簡単だけど、このフローチャートの意味がよくわからない。というのはつまり正負の符号を含む値の表現方法を理解していないということになる。説明はF.1.2.1節とTable F.1にある。
ここでSSSS
はRECEIVE
の引数で、読み取ったビット数T
のこと。DIFF
はRECEIVE
で読み取った値V
のこととなる。
SSSS = 0の場合、ストリームを読み取ることなくそのまま0となる。
SSSS = 1 の場合、つまり1ビット読み取った場合、1ビットには -1, 1 という2つの値が割り当てられている。これを小さい順に符号に割り当てる。
T | V | value |
---|---|---|
1 | 02 | -1 |
1 | 12 | 1 |
SSSS = 2 の場合、2ビットには -3, -2, 2, 3 という4つの値が割り当てられているので、これを小さい順に符号に割り当てる。
T | V | value |
---|---|---|
2 | 002 | -3 |
2 | 012 | -2 |
2 | 102 | 2 |
2 | 112 | 3 |
以下同じ。
これをアルゴリズムで表すとFigure F.12のフローチャートになる。表を見ると正の数の時 = 最上位ビットが1の時 = V < 2T-1でない時、戻り値はVの値そのままになっていることがわかる。
このように数値をコード化している理由は、0もしくは絶対値が小さい値の出現確率が極端に多く、かつ表現する値域が広いためハフマン符号化するとテーブルが大きくなりすぎるためだと思われる。差分符号化なので絶対値が小さい値が多いのだろう。
実装は以下のようにした。ほぼフローチャートのまま。
func extend(v_ uint8, t int) int16 { var v int16 = int16(v_) var vt int16 = 1 << (t - 1) if v < vt { return v + (-1 << t) + 1 } return v }