The MATLAB code on this page simulates the effects of one compression/decompression cycle with the JPEG codec.
Code samples on this website come without any guarantees – use them at your own risk! I am grateful for comments or patches sent to my email address: first name.last name@cl.cam.ac.uk.
function jpeg_result = jpeg_compression_cycle(original)
% Transform matrices
dct_matrix = dctmtx(8);
dct = @(block_struct) dct_matrix * block_struct.data * dct_matrix';
idct = @(block_struct) dct_matrix' * block_struct.data * dct_matrix;
% Quantization tables
q_max = 255;
q_y = ...
[16 11 10 16 124 140 151 161;
12 12 14 19 126 158 160 155;
14 13 16 24 140 157 169 156;
14 17 22 29 151 187 180 162;
18 22 37 56 168 109 103 177;
24 35 55 64 181 104 113 192;
49 64 78 87 103 121 120 101;
72 92 95 98 112 100 103 199];
q_c = ...
[17 18 24 47 99 99 99 99;
18 21 26 66 99 99 99 99;
24 26 56 99 99 99 99 99;
47 66 99 99 99 99 99 99;
99 99 99 99 99 99 99 99;
99 99 99 99 99 99 99 99;
99 99 99 99 99 99 99 99;
99 99 99 99 99 99 99 99];
% Scale quantization matrices based on quality factor
qf = 20;
if qf < 50
q_scale = floor(5000 / qf);
else
q_scale = 200 - 2 * qf;
end
q_y = round(q_y * q_scale / 100);
q_c = round(q_c * q_scale / 100);
% RGB to YCbCr
ycc = rgb2ycbcr(im2double(original));
% Down-sample and decimate chroma
cb = conv2(ycc(:, :, 2), [1 1; 1 1]) ./ 4.0;
cr = conv2(ycc(:, :, 3), [1 1; 1 1]) ./ 4.0;
cb = cb(2 : 2 : size(cb, 1), 2 : 2 : size(cb, 2));
cr = cr(2 : 2 : size(cr, 1), 2 : 2 : size(cr, 2));
y = ycc(:, :, 1);
% Discrete cosine transform, with scaling before quantization.
y = blockproc(y, [8 8], dct) .* q_max;
cb = blockproc(cb, [8 8], dct) .* q_max;
cr = blockproc(cr, [8 8], dct) .* q_max;
% Quantize DCT coefficients
y = blockproc(y, [8 8], @(block_struct) round(round(block_struct.data) ./ q_y));
cb = blockproc(cb, [8 8], @(block_struct) round(round(block_struct.data) ./ q_c));
cr = blockproc(cr, [8 8], @(block_struct) round(round(block_struct.data) ./ q_c));
% Dequantize DCT coefficients
y = blockproc(y, [8 8], @(block_struct) block_struct.data .* q_y);
cb = blockproc(cb, [8 8], @(block_struct) block_struct.data .* q_c);
cr = blockproc(cr, [8 8], @(block_struct) block_struct.data .* q_c);
% Inverse discrete cosine transform
y = blockproc(y ./ q_max, [8 8], idct);
cb = blockproc(cb ./ q_max, [8 8], idct);
cr = blockproc(cr ./ q_max, [8 8], idct);
% Up-sample chroma
upsample_filter_1d = [1 3 3 1] / 4;
upsample_filter = upsample_filter_1d' * upsample_filter_1d;
cb = conv2(upsample_filter, upsample(upsample(padarray(cb, [1 1], 'replicate'), 2)', 2)');
cb = cb(4 : size(cb,1) - 4, 4 : size(cb, 2) - 4);
cr = conv2(upsample_filter, upsample(upsample(padarray(cr, [1 1], 'replicate'), 2)', 2)');
cr = cr(4 : size(cr,1) - 4, 4 : size(cr, 2) - 4);
% Concatenate the channels to get the resulting image.
jpeg_result = ycbcr2rgb(cat(3, y, cb, cr));
end