TSG 部報 第 198 号・一般記事


一般記事(別ページ)


ごみ.JPG

TEA

1. はじめに

 どもTEAです。6月13日に98用のビデオ取り込みボードを買いました。 カノープスの IMAGEPAQ-98 なんですが、それでいろいろ取り込んでいたら、jpeg ローダーが作りたくなりました。ずっと前から、いつかは作りたいなあと思って いたのですが、仕組みがよくわからないし、PC-286 だと16色しかでないので 作る気が起きませんでした。

 jpeg ローダーを作ろうと決めたのは、東京BBSでしゅいらさんや ARGON さんが小さい jpeg ローダーを作っていて、実は jpeg は簡単なのかなと 思ったからですが、その他にも、去年の駒祭のころにICHI君が MATE 用の jpeg ローダーを作っていて、256色でも結構見られるんだなあと思ったこととか、 ぼくが MATE を買って256色モードが使えるようになったこともあります。 あと、IMAGEPAQについてきた jpeg ローダーがかなり高速で、もえ〜だったと いうのもあります。

 というわけで、これから何回かに分けて、jpeg ローダーの作り方を書こうと思います。 jpeg の表示には、ハフマン符号の復号、IDCT、RGBへの変換、減色が必要ですが、 まず1回目は、一番時間がかかると思われる、IDCT(逆離散コサイン変換)について 書こうと思います。

2. IDCT

 IDCT の処理では、周波数で表している波を普通の2次元の画像に戻します。 jpeg では、この処理を 8 × 8 ドットのブロックで行います。 IDCT は、次のような式で表されます。



S'yu と Syx の式はほとんど同じなので、後者だけ考えます。 三角関数を、
cv = √2 cos (vπ/16) とすると、Syx は 次のように表せます。

 よって、この式のまま計算すると64回の掛け算が必要となります。そこで、この式を 変形して計算を簡単にしたいわけです。

 まず、c4=1 ですから、ここでは掛け算はいりません。また、この行列に 現れる係数は、c1,c2,c3,c5,c6,c7 の6種類しかないので、 掛け算用のテーブルを作ることもできます。 次に、上の行列を偶数列と奇数列に分けると、次のようになります。

 つまり、もとの行列の上半分と下半分が全く同じか符号が違うだけなので、 一度行った計算が他のところに使えるようになり、掛け算を減らすことが できるわけです。偶数列に対しては、同じように4で割った余りで列を分ければ さらに掛け算が減ります。奇数列も同じようなことができますが、掛ける数の種類が 増えてしまうのでやめておきます。

 以上のようにすると、Sy0, …, Sy7 の計算は20回の掛け算でできることに なります。え? 掛け算は11回ですむはずだって? うーむ。まあいいでしょう(笑)。

3. いらない計算を無くそう

 さて、ここからが本題だったりします。 次回以降に説明しますが、jpeg では、周波数のデータは量子化(スケーリングと言った ほうがわかりやすいかな)されているので、Fvu のなかには 0 になって いるものが多いわけです。そして、Fvu は、次の順番でビットストリーム に入っています。

 つかれた(笑)。つまり、F00,F01,F10,F20, F11,F20,… という順番で入っています。そして、終了の 符号のあとはすべて 0 になっています。たとえば 35 (F70) で終わりだと すると、行列の右下半分はすべて 0 になっているわけです。よって、データの数に よって計算するルーチンを分ければいいのです。

 例えば、F00 だけが 0 でないとすると、 S'00 = S'01 = S'02 = S'03 = S'04 = S'05 = S'06 = S'07 = F00 となり、ほかの S'yu は 0 になります。jpeg で圧縮率が良い絵の場合、 この周波数のデータが 3 つしかないというところがわりとあります。ですから データによってルーチンを分ける方法はかなり有効のようです。

 さて、ぼくのプログラムがどうなっているかです。 まず Fvu から S'yu を計算する部分ですが、 64通りのルーチンを作ると なんかプログラムがでかくなりそうでいやなので (なんか昔と考え方が違うな)、 行に関して上から何番目まで 0 でないデータが入っているかによって8通りの ルーチンを作り、それを8回呼ぶようにしました。例えば、0 から 5 まで データが入っている場合は、1 列目が 3 個、2 列目が 2 個、3 列目が 1 個なので それぞれに対応するルーチンを呼ぶようになっています。

 S'yu から Syx を計算する部分は、上とは違って各列ごとに 0 でないデータの数が違うなんてことはないのですが、 ある行より下は全て 0 になっていることがあります。上の 0 から 5 までの例では、 Fvu が 3 列目までしかないので、S'yu は 3 行目までしかないと いうことになります。よってこちらは 64 通りではなく 8 通りに分けるわけです。 なお、こうすることで S'yu に現れる 0 をメモリに書かなくてすむこと にも注意してください。ついでに、F00 だけが 0 でないばあいは Syx は 64個全てが F00/8 と等しくなるので、この場合だけはサブルーチンを 8+1 回呼ぶようにはせずに専用のルーチンにしています。

4. 実際のプログラム

Fvu に 0 以外のデータが 6 つ(0 から 5)のときのプログラムの 一部を載せます(核爆)。



even
de05:
        push    offset idctsub22
        push    offset idctsub10
        push    offset idctsub11
        jmp     idctsub12
even
idctsub22:
        assume ds:_tbl,es:_tbl,ss:_dat
        mov     di,blk
        mov     si,offset dcttmp
        mov     di,dctob[di]
        mov     k,8
id221:  mov     bx,[si+2*16]
        add     bx,bx
        mov     cx,[si+0*16]
        mov     ax,dctmul2[bx] ; c2
        mov     bp,dctmul6[bx] ; c6
        mov     bx,[si+1*16]
        add     bx,bx
        add     cx,ax
        mov     dx,dctmul1[bx]
        add     cx,dx
        mov     [di+0*2],cx
        sub     cx,dx
        sub     cx,dx
        mov     [di+7*2],cx
        add     cx,dx
        sub     cx,ax
        sub     cx,ax
        mov     dx,dctmul7[bx]
        add     cx,dx
        mov     [di+3*2],cx
        sub     cx,dx
        sub     cx,dx
        mov     [di+4*2],cx
        add     cx,dx
        add     cx,ax
        add     cx,bp
        mov     dx,dctmul3[bx]
        add     cx,dx
        mov     [di+1*2],cx
        sub     cx,dx
        sub     cx,dx
        mov     [di+6*2],cx
        add     cx,dx
        sub     cx,bp
        sub     cx,bp
        mov     dx,dctmul5[bx]
        add     cx,dx
        mov     [di+2*2],cx
        sub     cx,dx
        sub     cx,dx
        mov     [di+5*2],cx
        add     si,2
        add     di,16
        dec     k
        jnz     short id221
        ret

5. 次回の予定

 今回は行列とかソースばっかりで、文字が少なかったですね。 次回はハフマン符号の復号のところを書こうと思います。

6. 連絡先

 感想などは、sada@is.s.u-tokyo.ac.jp までお願いします。 また、ぼくの作ったプログラムは http://naomi.is.s.u-tokyo.ac.jp/~sada/ にありますので使ってみてください。


[総目次へ]

g541119@komaba.ecc.u-tokyo.ac.jp