Last modified: Sat Feb 9 15:09:56 UTC+0100 2019 © A. Tarpai
JPEG81 coefficient decoder
History
I've made a progressive JPEG decoder available many years ago (FireRainbow), but when I looked at my code after 5 years, well... it hit me by the classical programmer's saying: "Hm.. that's not my code". I've decided to re-write and extend a little bit. Back then, my motivation was to simply blit an uncomressed image into the frame buffer in my hobby OS. For others this might be not the case, although I tried to make the code as general as I could.
JPEG coefficient decoder
In JPEG81 I have focused on ITU T.81 decoder conformance. In the T.83 package there are several compressed test data streams (JPEG sample image files). For the DCT-based process, reference test data is in the form of 16-bit signed coefficients for each 8x8 block per image component in a separate file. During decoding, this is the stage right before de-quantization (then IDCT).
Therefore, JPEG81 is a 'coefficient decoder'.
It allowed me to test:
- parsing JPEG markers with or without 0xFF byte stuffing
- 4 image components with complex sub-sampling
- Restart Markers
- Huffmann ECS bit-stuffing
- partial MCU-s, DU- and sample extension (255 x 257 image size)
- single- and interleaved scans
- progressive mode
- arithmetic coding
- 8- and 12-bit sample precision
- the Lossless-mode
The following files were successfully decoded with no error against encoder reference data (files of EREF.DCT):
DATA JPEG Precision Entropy Code Nf Inter- DRI STREAM mode (bits) Coder Tables leave ----------------------------------------------------------------------- A1 S(B) 8 Huff sample 4 yes yes C1 S(E) 8 Huff custom 4 yes yes C2 S(E) 8 Huff custom 4 no yes D1 S(E) 8 arith custom 4 yes yes D2 S(E) 8 arith custom 4 no yes E1 S(E) 12 Huff custom 4 yes yes E2 S(E) 12 Huff custom 4 no yes F1 S(E) 12 arith custom 4 yes yes F2 S(E) 12 arith custom 4 no yes G1 P(SS) 8 Huff custom 4 yes yes H1 P(SS) 8 arith custom 4 yes yes J1 P(SS) 12 arith custom 4 yes yes K1 P(Full) 8 Huff custom 4 yes yes L1 P(Full) 8 arith custom 4 yes yes N1 P(Full) 12 arith custom 4 yes yes O1 LL 8 Huff sample 4 yes yes O2 LL 16 Huff sample 4 no yes P1 LL 8 arith default 4 yes yes P2 LL 16 arith custom 4 no yes
Those T.83 test images have 4 image components and a crazy sub-sampling (output from Analyzer):
0000021B: FFC2 SOF2 (Huffman Progressive DCT)
P=8 Y=257 X=255
Nf=4
Ci=200 HV=1x1 Qi=0
Ci=150 HV=1x2 Qi=1
Ci=100 HV=3x1 Qi=2
Ci= 50 HV=1x4 Qi=3
0
Sample: x=85+3 (1:3) y=65+7 (1:4)
8x8 Data Unit: 99 (X=11 Y=9)
MCU Data Unit: 99 (X=11 Y=9)
1
Sample: x=85+3 (1:3) y=129+7 (1:2)
8x8 Data Unit: 187 (X=11 Y=17)
MCU Data Unit: 198 (X=11 Y=18)
2
Sample: x=255+1 (1:1) y=65+7 (1:4)
8x8 Data Unit: 288 (X=32 Y=9)
MCU Data Unit: 297 (X=33 Y=9)
3
Sample: x=85+3 (1:3) y=257+7 (1:1)
8x8 Data Unit: 363 (X=11 Y=33)
MCU Data Unit: 396 (X=11 Y=36)
99 MCU (11 x 9)
Malloc for 990 Data Units (126720 bytes)
MCU: 3 x 4 (24 x 32 pixels)
DU: 0 1 1 2 2 2 3 3 3 3 (10 DU/MCU)
Subsampling:
+---------+---------+---------+---------+
| 0 - - | 1 - - | 2 2 2 | 3 - - |
| - - - | 1 - - | - - - | 3 - - |
| - - - | - - - | - - - | 3 - - |
| - - - | - - - | - - - | 3 - - |
+---------+---------+---------+---------+
During testing, no matter how hard I tried, I got +/- 1 decoded coefficient differences comparing my output with the Decoder reference test data. It made me crazy, at this stage there should be a match. Especially disturbing, because as in the last successive scan, when the lsb of the coefficient gets decoded, this error cannot detect decoding error. Then, I just tried the Encoder reference test data - and there were no errors, the two fully matched. The Encoder reference test data is different from the Decoder reference test data in +/- 1 at certain places.
Input
Through two statically linked functions by the moment:
- GETC() should return the next byte from the compressed data stream
- SEEK() ability to adjust the 'file pointer'
This interface fits both common scenarios:
- JPEG is in memory
- JPEG is read through stream file I/O
Output
For the DCT-based mode, memory structure is (almost) the same as defined in T.83. After reading the SOF header, the decoder allocates buffer for all Data Units of all components, preparing to receive partial MCU-s in one, big interleaved scan. This is the maximum number of DU-s for the image.
Here is the structure for one image component: 'X' are possible extensions in partial MCU-s.
for single scans, only DU-s marked by '-' are in encoded in the data stream
for interleaved scans, there is a possibility for 'dummy' DU-s, marked by 'X'
- - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X - - - - - - - X X X X X X X X X X X
What is it good for?
So this version is a stripped-down decoder. It's a one-file-lib, with very simple I/O interface. I used it with modifications for a small jpeg-to-bmp decoder, a jpeg-analyzer console application, and a jpeg-analyzer Windows GUI application. As I mentioned, it really depends what we want from a JPEG decoder.
Source code
Can be found in RAINBOW on GitHub.