これまでのJPEGシリーズ
- 【調査中】JPEG画像を無劣化(ロスレス)で切り貼りしたい【ハフマン符号】 #画像処理 - Qiita
- JPEG画像を無劣化(ロスレス)で切り貼りしたい ~ハフマン符号の読み方の答え~ #画像処理 - Qiita
- JPEG画像の圧縮データにおける各コンポーネントのデータ数と配置の求め方 #画像処理 - Qiita
- JPEG画像の圧縮データのエンコード・デコード方法とブロックの並び順 #画像処理 - Qiita
今回やること
JPEG画像におけるリスタートマーカーの使われ方を観察し、処理方法を確認する。
リスタートマーカーに関する情報
file - Jpeg restart markers - Stack Overflow
より、リスタートマーカーについて以下のことがわかる。
- エラーからの復帰を目的に設計された
- マーカー
FFDDでリスタートの間隔 (MCU何個ごとにリスタートマーカーを入れるか) を定義する - 圧縮データ中にリスタートマーカー (
FFD0~FFD7) があったら、以下を行う- 各チャンネルの「前のブロックの DC 成分」を 0 にリセットする
- 圧縮データの読み取りをマーカーの次のデータ境界から始める
-
FFD0からFFD7を順に使っていき、FFD7の次はFFD0に戻る- これにより、リスタートマーカー自体の欠落の検出を狙う
- マルチスレッドでデコードを行う際、スレッドへのデコードする範囲の割り当てにも活用できる
リスタートマーカーの観察
GIMP 2.10.38 で真っ白な画像を作り、JPEGにエクスポートを行った。
シンプルな画像 (グレースケール)
32×32 のグレースケールで、リスタートマーカーをオン、「間隔 (MCU 数)」を 1 に設定してエクスポートを行うと、以下の DRI (リスタートマーカーの間隔の定義) が出力された。(TSXBIN で観察を行った)
93 ★DRI[0] FF DD
95 SizeOfThis[0] 00 04
97 RestartInterval[0] 00 04
ここから、リスタート間隔は 4 となったことが読み取れる。
なぜか、設定した間隔に1行あたりの「まとまり」の数を掛けた値が、実際のリスタート間隔になるようである。
また、圧縮データは以下のようになった。
A3 圧縮データ(18B)[0] AA 60 3F FF D0 AA 60 3F FF D1 AA 60 3F FF D2 AA
B3 圧縮データ(18B)[16] 60 3F
AA 60 3F が4回現れ、その間にリスタートマーカー (FF Dx) が挟まっている。
これを JPEGsnoop で見ると、圧縮データの冒頭は以下のようになった。
Lum (Tbl #0), MCU=[0,0]
[0x000000A3.0]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x AA 60 3F FF = 0b (10101010 011----- -------- --------)]
[0x000000A4.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 3F FF D0 = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[1,0]
[0x000000A4.4]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 3F FF D0 = 0b (----0--- -------- -------- --------)] EOB
[0x000000A4.5]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 3F FF D0 = 0b (-----0-- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[2,0]
[0x000000A4.6]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 3F FF D0 = 0b (------0- -------- -------- --------)] EOB
[0x000000A4.7]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 3F FF D0 = 0b (-------0 -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[3,0]
[0x000000A5.0]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 3F FF D0 AA = 0b (0------- -------- -------- --------)] EOB
[0x000000A5.1]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 3F FF D0 AA = 0b (-0------ -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,1]
[0x000000A5.2]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x 3F FF D0 AA = 0b (--111111 11111--- -------- --------)]
[0x000000A9.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 3F FF D1 = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
最初のブロックで大きい値 1017 が現れたあと、続く3ブロックでは 0 ばかりが現れ、その次のブロックでまた 1017 が現れている。
これは、リスタート間隔が 4 であるため、4 ブロックごとに「前回の値」のリセットが行われることに対応する。
リスタートマーカーの後では、なぜかデータの表示がおかしくなっている。
縦長画像 (グレースケール)
なぜか画像の横幅に応じて設定した値より大きいリスタート間隔が用いられてしまうことがわかったので、今度は 8×1024 の画像を作成した。
DRI は以下のようになり、リスタート間隔は 3 であることがわかる。
093 ★DRI[0] FF DD
095 SizeOfThis[0] 00 04
097 RestartInterval[0] 00 03
圧縮データは、以下のようになった。
0A3 圧縮データ(170B)[0] AA 60 FF D0 AA 60 FF D1 AA 60 FF D2 AA 60 FF D3
0B3 圧縮データ(170B)[16] AA 60 FF D4 AA 60 FF D5 AA 60 FF D6 AA 60 FF D7
0C3 圧縮データ(170B)[32] AA 60 FF D0 AA 60 FF D1 AA 60 FF D2 AA 60 FF D3
0D3 圧縮データ(170B)[48] AA 60 FF D4 AA 60 FF D5 AA 60 FF D6 AA 60 FF D7
0E3 圧縮データ(170B)[64] AA 60 FF D0 AA 60 FF D1 AA 60 FF D2 AA 60 FF D3
0F3 圧縮データ(170B)[80] AA 60 FF D4 AA 60 FF D5 AA 60 FF D6 AA 60 FF D7
103 圧縮データ(170B)[96] AA 60 FF D0 AA 60 FF D1 AA 60 FF D2 AA 60 FF D3
113 圧縮データ(170B)[112] AA 60 FF D4 AA 60 FF D5 AA 60 FF D6 AA 60 FF D7
123 圧縮データ(170B)[128] AA 60 FF D0 AA 60 FF D1 AA 60 FF D2 AA 60 FF D3
133 圧縮データ(170B)[144] AA 60 FF D4 AA 60 FF D5 AA 60 FF D6 AA 60 FF D7
143 圧縮データ(170B)[160] AA 60 FF D0 AA 60 FF D1 AA 63
FF D0 ~ FF D7 が順番に繰り返し使われていることがわかる。
また、リスタートマーカーの間のデータは、最後以外は AA 60 であるが、最後は AA 63 となっている。
今回のブロック数は 128 なので、3 ブロックずつ格納していくと最後に 2 ブロック余ることになる。
最後のデータでは、この 2 ブロックが格納され、余計なブロックは格納されていないことがわかる。
[Python] JPEG Encoderを実装しながら追うJPEGのファイル仕様 | henatips
より、最後のバイトの余ったビットは 1 で埋めることがわかり、それが反映されている。
この「枠が余っても余計なブロックは格納されない」というのは、画像から完全にはみ出す部分があったとしてもはみ出さない場合と同様に全部のブロックを格納する「まとまり」とは異なる動作である。
縦長カラー画像
画像のサイズは同じく 8×1024 とし、今度はカラー画像にしてみた。
DRI は以下のようになり、リスタート間隔は 3 である。
10A ★DRI[0] FF DD
10C SizeOfThis[0] 00 04
10E RestartInterval[0] 00 03
圧縮データは、以下のようになった。
11E 圧縮データ(255B)[0] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 00 0F
12E 圧縮データ(255B)[16] FF D2 AA 60 00 0F FF D3 AA 60 00 0F FF D4 AA 60
13E 圧縮データ(255B)[32] 00 0F FF D5 AA 60 00 0F FF D6 AA 60 00 0F FF D7
14E 圧縮データ(255B)[48] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 00 0F
15E 圧縮データ(255B)[64] FF D2 AA 60 00 0F FF D3 AA 60 00 0F FF D4 AA 60
16E 圧縮データ(255B)[80] 00 0F FF D5 AA 60 00 0F FF D6 AA 60 00 0F FF D7
17E 圧縮データ(255B)[96] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 00 0F
18E 圧縮データ(255B)[112] FF D2 AA 60 00 0F FF D3 AA 60 00 0F FF D4 AA 60
19E 圧縮データ(255B)[128] 00 0F FF D5 AA 60 00 0F FF D6 AA 60 00 0F FF D7
1AE 圧縮データ(255B)[144] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 00 0F
1BE 圧縮データ(255B)[160] FF D2 AA 60 00 0F FF D3 AA 60 00 0F FF D4 AA 60
1CE 圧縮データ(255B)[176] 00 0F FF D5 AA 60 00 0F FF D6 AA 60 00 0F FF D7
1DE 圧縮データ(255B)[192] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 00 0F
1EE 圧縮データ(255B)[208] FF D2 AA 60 00 0F FF D3 AA 60 00 0F FF D4 AA 60
1FE 圧縮データ(255B)[224] 00 0F FF D5 AA 60 00 0F FF D6 AA 60 00 0F FF D7
20E 圧縮データ(255B)[240] AA 60 00 0F FF D0 AA 60 00 0F FF D1 AA 60 03
JPEGsnoop で見ると、圧縮データの冒頭は以下のようになっていた。
Lum (Tbl #0), MCU=[0,0]
[0x0000011E.0]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x AA 60 00 0F = 0b (10101010 011----- -------- --------)]
[0x0000011F.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 0F FF = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,0]
[0x0000011F.4]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 00 0F FF = 0b (----0--- -------- -------- --------)] EOB
[0x0000011F.5]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 0F FF = 0b (-----0-- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,0]
[0x0000011F.6]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 00 0F FF = 0b (------0- -------- -------- --------)] EOB
[0x0000011F.7]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 0F FF = 0b (-------0 -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,1]
[0x00000120.0]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 0F FF D0 = 0b (0------- -------- -------- --------)] EOB
[0x00000120.1]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 0F FF D0 = 0b (-0------ -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,1]
[0x00000120.2]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 0F FF D0 = 0b (--0----- -------- -------- --------)] EOB
[0x00000120.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 0F FF D0 = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,1]
[0x00000120.4]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 0F FF D0 = 0b (----0--- -------- -------- --------)] EOB
[0x00000120.5]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 0F FF D0 = 0b (-----0-- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,2]
[0x00000120.6]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 0F FF D0 = 0b (------0- -------- -------- --------)] EOB
[0x00000120.7]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 0F FF D0 = 0b (-------0 -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,2]
[0x00000121.0]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 0F FF D0 AA = 0b (0------- -------- -------- --------)] EOB
[0x00000121.1]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 0F FF D0 AA = 0b (-0------ -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,2]
[0x00000121.2]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 0F FF D0 AA = 0b (--0----- -------- -------- --------)] EOB
[0x00000121.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 0F FF D0 AA = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,3]
[0x00000121.4]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x 0F FF D0 AA = 0b (----1111 1111111- -------- --------)]
[0x00000125.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 0F FF = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
「Lum 1個とChr 2個のセット」3個ごとにリスタートマーカーが入り、「前のブロックの値」がリセットされていることがわかる。
サブサンプリングを行う画像
画像のサイズを再び 32×32 とし、「間隔 (MCU 数)」は 1、サブサンプリングを「4:2:2 水平 (1/2 クロマ)」にして画像を出力した。
DRI は以下のようになり、リスタート間隔は 2 である。
10A ★DRI[0] FF DD
10C SizeOfThis[0] 00 04
10E RestartInterval[0] 00 02
圧縮データは以下のようになった。
11E 圧縮データ(22B)[0] AA 60 00 3F FF D0 AA 60 00 3F FF D1 AA 60 00 3F
12E 圧縮データ(22B)[16] FF D2 AA 60 00 3F
JPEGsnoop で圧縮データを見ると、冒頭は以下のようになった。
Lum (Tbl #0), MCU=[0,0]
[0x0000011E.0]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x AA 60 00 3F = 0b (10101010 011----- -------- --------)]
[0x0000011F.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 3F FF = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,0]
[0x0000011F.4]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 00 3F FF = 0b (----0--- -------- -------- --------)] EOB
[0x0000011F.5]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 3F FF = 0b (-----0-- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,0]
[0x0000011F.6]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 60 00 3F FF = 0b (------0- -------- -------- --------)] EOB
[0x0000011F.7]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 3F FF = 0b (-------0 -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[0,0]
[0x00000120.0]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 3F FF D0 = 0b (0------- -------- -------- --------)] EOB
[0x00000120.1]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 3F FF D0 = 0b (-0------ -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[1,0]
[0x00000120.2]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 3F FF D0 = 0b (--0----- -------- -------- --------)] EOB
[0x00000120.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 3F FF D0 = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[1,0]
[0x00000120.4]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 3F FF D0 = 0b (----0--- -------- -------- --------)] EOB
[0x00000120.5]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 3F FF D0 = 0b (-----0-- -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[1,0]
[0x00000120.6]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 00 3F FF D0 = 0b (------0- -------- -------- --------)] EOB
[0x00000120.7]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 00 3F FF D0 = 0b (-------0 -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Chr(0) (Tbl #1), MCU=[1,0]
[0x00000121.0]: ZRL=[ 0] Val=[ 0] Coef=[00= DC] Data=[0x 3F FF D0 AA = 0b (0------- -------- -------- --------)] EOB
[0x00000121.1]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 3F FF D0 AA = 0b (-0------ -------- -------- --------)] EOB
DCT Matrix=[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
Lum (Tbl #0), MCU=[0,1]
[0x00000121.2]: ZRL=[ 0] Val=[ 339] Coef=[00= DC] Data=[0x 3F FF D0 AA = 0b (--111111 11111--- -------- --------)]
[0x00000125.3]: ZRL=[ 0] Val=[ 0] Coef=[01..01] Data=[0x 60 00 3F FF = 0b (---0---- -------- -------- --------)] EOB
DCT Matrix=[ 1017 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
今回は、Lum 2個と、Chr 1個×2要素=2個で1個の「まとまり」となる。
リスタート間隔が、リスタートマーカー間のこの「まとまり」の数に反映されることがわかる。
細かい検証
「間隔 (MCU 数)」を 4 にして 32×32 のJPEG画像を出力すると、リスタート間隔は 16 となり、圧縮データ中にリスタートマーカーはみられなかった。
これにより、リスタート間隔が「まとまり」の数の約数のときも、最後の「まとまり」の後にリスタートマーカーは入れないことがわかる。
4096×4096 のJPEG画像を (リスタートマーカーなしで) 出力し、リスタート間隔 0 を表す DRI を手動で挿入しても、加工後の画像ファイルは ImageMagick で正常に読み込めた。
このような画像ファイルのブロック数は 65,536 を超えているため、リスタート間隔 0 は「リスタートマーカーなし」を表すらしいことがわかる。
まとめ
- リスタートマーカーを用いる場合、圧縮データ (SOS) より前に、DRI (
FF DD) により何個の「まとまり」に対応するデータを記録するごとにリスタートマーカーを挟むか (リスタート間隔) を定義する- この数が 0 の場合、「リスタートマーカーを用いない」ことを表す
- リスタートマーカーは、
FF D0~FF D7を順に繰り返し用いる - リスタートマーカーを挟んだ時、「前のブロックの DC 成分」は全チャンネルで 0 にリセットする
- 「まとまり」の数がリスタート間隔の整数倍のときでも、最後の「まとまり」のデータの後にリスタートマーカーは挟まない
- 「まとまり」の数がリスタート間隔の整数倍でないときでも、パディングはしない