さて、お次は量子化処理だ。
そして、この工程をもって小数値をフォローするために行っていた
ケタ上げを解除する。
まず、ハナシとして簡単なAC成分の量子化から…
量子化はDCT変換した値を各成分毎に対応する量子化係数値で
割った商を整数値で確保するってのが基本。
で、本dllの場合は量子化係数値はJpegヘッダとなるJPHead[ ]配列に
Y成分はJPHead[25]から、CbCr成分はJPHead[94]から入っているので
コレを使ってDCT変換値を割って商を得る。
さらに本dll ではAC成分は21bitケタ上げしてあるから、
得た商を21bitのビットシフトを行って、このケタ上げを解除する。
この時、0.5x221を足してからビットシフトを行うことで
四捨五入を行っている。
ファンクションのデータの流れとしては、
DCT変換値をジグザク並べ替えしたデータ列をZig[ ][ ]配列で受け取り
量子化処理した値を同じZig[ ][ ]配列に格納して返す。
問題なのはDC成分の量子化だ…
大抵のJpegの解説では
「DC係数は直前の処理ブロックのDC係数との差分を量子化処理する」
と一言で書いてある。
ワタシは怒りをこめて言いたい。
「オマエ本当にJpeg保存プログラム作ったことあるのかよ!」
と。
ワタシはこの一言のために数週間悩まされた。
普通、「直前の処理ブロックのDC係数との差分を量子化」
って言われたらさぁ…
DCT変換後のDC成分値を一旦変数に確保しておいて
次のブロックのDC処理時にその変数から取り出した値との差を取って
ソレをAC成分同様に処理すれば良いと思うじゃない?
(ケタ下げ量は差分処理でのオーバーフローを避けるために
ACより1bitだけ既にケタ下げしてあるので20bit下げだが)
で、当初以下のコードを作ったのだが、
コレがNGなのである。
/* NG */
void Quantize
(long Zig[][3], long DC[]){
int k, n;
long Temp;
// ***** for Y *****
Temp = Zig[0][0];
Zig[0][0] = (((Zig[0][0] - DC[0]) / JPHead[25]) + 524288) >> 20;
DC[0] = Temp; // Set Previous DC
for (n = 1; n < 64; n++){
Zig[n][0] = ((Zig[n][0] / JPHead[25 + n]) + 1048576) >> 21;
}
// ***** for Cb,Cr *****
for (k = 1; k < 3; k++){
Temp = Zig[0][k];
Zig[0][k] = (((Zig[0][k] - DC[k]) / JPHead[94]) + 524288) >> 20;
DC[k] = Temp; // Set Previous DC
for (n = 1; n < 64; n++){
Zig[n][k] = ((Zig[n][k] / JPHead[94 + n]) + 1048576) >> 21;
}
}
}
このコードのどこがどうNGなのか…なのだが
まず、どういう問題が起きたのかを紹介しよう。
下の画像を…
Q値=95でJpegにすると下の画像ができた。
特に違和感は感じない。
しかしQ値=65でJpegにすると…
中央から右端にかけてうっすらとストライプ状のノイズが乗っている。
このノイズが低Q値に伴うJpegノイズと考えるには疑問がある。
まず、ノイズが「ブロック」ではなく、「ストライプ」なのが怪しい。
さらに、どんな画像をもとにしてもストライプは中央から右端で目立ち、
左端付近にはほとんど見られないというのも怪しい。
そこでデバッグとなるが、どこに問題があるのか一向にわからない。
原因がDC差分処理と特定するまで数週間かかった。
DC差分処理をどう直せばいいんだと思う?
- つづく -