A complete implementation of the JPEG compression pipeline β encoding and decoding β built from scratch using NumPy and OpenCV.
This project implements the full JPEG compression and decompression pipeline without relying on any JPEG library. It demonstrates the mathematical foundations behind one of the world's most widely used image formats, achieving compression ratios of up to 10.3:1 on standard test images.
Original Image (RGB)
β
βΌ
βββββββββββββββββββββββ
β Color Space β RGB β YCbCr (separates luma from chroma)
β Conversion β
ββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Block Splitting β Divide into 8Γ8 macroblocks
ββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β DCT (2D) β Spatial β Frequency domain
ββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Quantization β Lossy step β discard high-freq info
ββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Zig-Zag Scanning β 2D β 1D, groups zeros together
ββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Run-Length Encoding β Lossless compression of zero runs
ββββββββββ¬βββββββββββββ
β
βΌ
Compressed Bitstream
βΌ (Decoding reverses all steps)
Reconstructed Image
By default it loads cameraman.bmp. To use a different image, edit the cv2.imread(...) call at the bottom of the script.
Converts the BGR image (OpenCV default) to YCbCr, separating luminance (Y) from chrominance (Cb, Cr). The human visual system is less sensitive to color detail than brightness, so chroma channels can be compressed more aggressively.
Y = 0.299Β·R + 0.587Β·G + 0.114Β·B
Cb = 128 β 0.168736Β·R β 0.331264Β·G + 0.5Β·B
Cr = 128 + 0.5Β·R β 0.418688Β·G β 0.081312Β·B
Pads the image (if needed) to a multiple of 8, then slices each channel into non-overlapping 8Γ8 blocks. Each block is processed independently.
Applies the 1D DCT row-wise then column-wise. The DCT concentrates most image energy into a small number of low-frequency coefficients, making subsequent quantization effective.
Before DCT, each block is level-shifted by β128 (centering the range from [0, 255] to [β128, 127]).
Each DCT coefficient is divided by a corresponding entry in the standard JPEG quantization table and rounded to the nearest integer. Higher-frequency entries in the table are larger, aggressively discarding perceptually unimportant data.
| Table | Used for |
|---|---|
quantization_Y |
Luminance (Y channel) |
quantization_C |
Chrominance (Cb, Cr channels) |
Reorders the 8Γ8 quantized block into a 1D array following a zig-zag path. This groups the typically-nonzero low-frequency coefficients at the start and the many zeros at the end β ideal for RLE.
Encodes runs of zeros between non-zero AC coefficients as (RUNLENGTH, SIZE, AMPLITUDE) triplets, where:
RUNLENGTH= number of preceding zerosSIZE= bit-width of the amplitudeAMPLITUDE= the coefficient value
Compression Ratio = bits_original / bits_compressed
The original image requires H Γ W Γ 3 Γ 8 bits. The compressed representation uses the total bits across RLE triplets for Y, Cb, and Cr.
The decoding pipeline reverses every step:
Inverse Zig-Zag β Dequantization β Inverse DCT β Level Shift (+128) β Merge Blocks β YCbCr β BGR
Test images used: corn.bmp, pepper.bmp, cameraman.bmp
| Image | Original Size | Compression Ratio |
|---|---|---|
| corn.bmp | β | 5.1 : 1 |
| pepper.bmp | β | 8.1 : 1 |
| cameraman.bmp | β | 10.3 : 1 |
The reconstructed image is visually close to the original with the standard quantization tables, consistent with real-world JPEG behavior (typically 10:1 with minimal perceptible loss).
| Function | Description |
|---|---|
BGR_to_YCbCr(img) |
Color space conversion |
macroblock(img, channel) |
Split channel into 8Γ8 blocks |
dct_1d(x) |
1D Discrete Cosine Transform |
dct_2d(img) |
2D DCT via separable 1D DCT |
zig_zag_scanning(x) |
2D block β 1D zig-zag ordered array |
run_length_encoding(x) |
RLE on zig-zag scanned coefficients |
compression(image) |
Full encoding pipeline |
inverse_zig_zag_scanning(x, n) |
Reverse zig-zag |
inverse_dct_1d(x) |
Inverse 1D DCT |
inverse_dct_2d(img) |
Inverse 2D DCT |
decompression(e_Y, e_Cb, e_Cr, ...) |
Full decoding pipeline |
YCbCr_to_BGR(ycbcr) |
Inverse color space conversion |
max_bits(x) |
Estimate bits needed for RLE output |
- Wallace, G.K. (1991). The JPEG still picture compression standard. IEEE Transactions on Consumer Electronics.
- Wikipedia β JPEG
- Wikipedia β Discrete Cosine Transform
- Wikipedia β Run-length encoding
This project is for academic purposes. Feel free to use, study, and extend it.