Computer Vision
Homework 5:
JPEG Compression
Pesented by:
Zahraa muhaned mohammed faeq
MSc stg 1
1&2
jpeg _compression.m
function jpeg_compression(image_path, Q, downsamplingMode)
% 1. Read the image and convert it to YCbCr color space
img = imread(image_path);
img_ycbcr = rgb2ycbcr(img);
Y = img_ycbcr(:,:,1);
Cb = img_ycbcr(:,:,2);
Cr = img_ycbcr(:,:,3);
% 2. Zero-pad image components so their dimensions are divisible by 8 (or 16)
padToMultiple = @(channel, factor) padarray(channel, ...
mod(-size(channel), factor), 'post');
Y = padToMultiple(Y, [8, 8]);
Cb = padToMultiple(Cb, [8, 8]);
Cr = padToMultiple(Cr, [8, 8]);
% 3. Apply chroma subsampling depending on selected mode
switch downsamplingMode
case 'none'
% No downsampling
case 'h'
% Horizontal only
Cb = imresize(Cb, [size(Cb,1), floor(size(Cb,2)/2)], 'bilinear');
Cr = imresize(Cr, [size(Cr,1), floor(size(Cr,2)/2)], 'bilinear');
case 'h+v'
% Horizontal and vertical
Cb = imresize(Cb, floor(size(Cb)/2), 'bilinear');
Cr = imresize(Cr, floor(size(Cr)/2), 'bilinear');
otherwise
error('Invalid downsampling mode. Use "none", "h", or "h+v".');
end
% 4. Generate quantization matrices based on quality factor
[Qy, Qc] = getQuantMatrices(Q);
%% 5. Apply DCT + quantization block-by-block using blockproc
Y_q = blockproc(Y, [8 8], @(b) jpegBlockDCT(b, Qy));
Cb_q = blockproc(Cb, [8 8], @(b) jpegBlockDCT(b, Qc));
Cr_q = blockproc(Cr, [8 8], @(b) jpegBlockDCT(b, Qc));
figure;
subplot(1, 2, 1);
imshow(img);
title('Original Image');
% Reconstruct the image after compression (decoding part)
img_reconstructed = jpeg_decompression(Y, Y_q, Cb_q, Cr_q, Q, downsamplingMode);
subplot(1, 2, 2);
imshow(img_reconstructed);
title('Compressed Image');
% Optional: You can save or visualize the compressed result here
disp('JPEG compression completed successfully.');
end
% Function to compute quantization matrices based on quality factor
function [Qy, Qc] = getQuantMatrices(Q)
% Standard quantization matrices
Qy_std = [
16 11 10 16 24 40 51 61;
12 12 14 19 26 58 60 55;
14 13 16 24 40 57 69 56;
14 17 22 29 51 87 80 62;
18 22 37 56 68 109 103 77;
24 35 55 64 81 104 113 92;
49 64 78 87 103 121 120 101;
72 92 95 98 112 100 103 99];
Qc_std = [
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];
% Compute scale factor
if Q < 50
S = 5000 / Q;
else
S = 200 - 2 * Q;
end
% Scale quantization matrices
Qy = round((S / 100) * Qy_std);
Qc = round((S / 100) * Qc_std);
end
% Function to apply range-shifting, DCT, and quantization to an 8x8 block
function outBlock = jpegBlockDCT(blockStruct, Qmat)
block = double(blockStruct.data) - 128; % Range shift to [-128, 127]
dct_block = dct2(block); % Apply 2D DCT
outBlock = round(dct_block ./ Qmat); % Quantize using Q matrix
end
function img_reconstructed = jpeg_decompression(Y, Y_q, Cb_q, Cr_q, Q, downsamplingMode)
% Reconstruct the quantization matrices
[Qy, Qc] = getQuantMatrices(Q);
% Apply inverse DCT and dequantization block-by-block
Y_dq = blockproc(Y_q, [8 8], @(b) jpegBlockInverseDCT(b, Qy));
Cb_dq = blockproc(Cb_q, [8 8], @(b) jpegBlockInverseDCT(b, Qc));
Cr_dq = blockproc(Cr_q, [8 8], @(b) jpegBlockInverseDCT(b, Qc));
% Resize the chroma channels to their original dimensions
switch downsamplingMode
case 'none'
% No resizing
case 'h'
Cb_dq = imresize(Cb_dq, [size(Cb_dq, 1), size(Cb_dq, 2) * 2], 'bilinear');
Cr_dq = imresize(Cr_dq, [size(Cr_dq, 1), size(Cr_dq, 2) * 2], 'bilinear');
case 'h+v'
Cb_dq = imresize(Cb_dq, [size(Cb_dq, 1) * 2, size(Cb_dq, 2) * 2], 'bilinear');
Cr_dq = imresize(Cr_dq, [size(Cr_dq, 1) * 2, size(Cr_dq, 2) * 2], 'bilinear');
otherwise
error('Invalid downsampling mode. Use "none", "h", or "h+v".');
end
% Reconstruct the full YCbCr image
img_ycbcr_reconstructed = cat(3, Y_dq, Cb_dq, Cr_dq);
% Convert YCbCr to RGB
img_reconstructed = ycbcr2rgb(img_ycbcr_reconstructed);
% Crop any padding added during compression
img_reconstructed = img_reconstructed(1:size(Y,1), 1:size(Y,2), :);
end
% Function to apply inverse DCT and dequantization to an 8x8 block
function outBlock = jpegBlockInverseDCT(blockStruct, Qmat)
block = double(blockStruct.data); % Get quantized block
block = block .* Qmat; % Dequantize using Q matrix
idct_block = idct2(block); % Apply inverse DCT
outBlock = uint8(idct_block + 128); % Range shift back to [0, 255]
end
In command window :
>> jpeg_compression('C:\Users\ahmed\Pictures\ph\2.jpg', 50, 'h+v');
JPEG compression completed successfully.
3. Choose an image, then apply the JPEG compression and decompression algorithms coded in steps 1
and 2 to plot the RMSE for each possible value of the quality factor Q.
function rmse_vs_q(image_path)
Q_values = 10:10:90;
RMSEs = zeros(size(Q_values));
original = im2double(imread(image_path));
for i = 1:length(Q_values)
Q = Q_values(i);
reconstructed = im2double(jpeg_compression(image_path, Q, 'h+v'));
RMSEs(i) = sqrt(mean((original(:) - reconstructed(:)).^2));
end
figure;
plot(Q_values, RMSEs, '-o');
xlabel('Quality Factor Q');
ylabel('RMSE');
title('RMSE vs JPEG Quality Factor');
grid on;
end
function img_reconstructed = jpeg_compression(image_path, Q, downsamplingMode)
img = imread(image_path);
img_ycbcr = rgb2ycbcr(img);
Y = img_ycbcr(:,:,1);
Cb = img_ycbcr(:,:,2);
Cr = img_ycbcr(:,:,3);
padToMultiple = @(channel, factor) padarray(channel, ...
mod(-size(channel), factor), 'post');
Y_padded = padToMultiple(Y, [8, 8]);
Cb_padded = padToMultiple(Cb, [8, 8]);
Cr_padded = padToMultiple(Cr, [8, 8]);
switch downsamplingMode
case 'none'
Cb_ds = Cb_padded;
Cr_ds = Cr_padded;
case 'h'
Cb_ds = imresize(Cb_padded, [size(Cb_padded,1),
floor(size(Cb_padded,2)/2)], 'bilinear');
Cr_ds = imresize(Cr_padded, [size(Cr_padded,1),
floor(size(Cr_padded,2)/2)], 'bilinear');
case 'h+v'
Cb_ds = imresize(Cb_padded, floor(size(Cb_padded)/2), 'bilinear');
Cr_ds = imresize(Cr_padded, floor(size(Cr_padded)/2), 'bilinear');
otherwise
error('Invalid downsampling mode. Use "none", "h", or "h+v".');
end
[Qy, Qc] = getQuantMatrices(Q);
Y_q = blockproc(Y_padded, [8 8], @(b) jpegBlockDCT(b, Qy));
Cb_q = blockproc(Cb_ds, [8 8], @(b) jpegBlockDCT(b, Qc));
Cr_q = blockproc(Cr_ds, [8 8], @(b) jpegBlockDCT(b, Qc));
img_reconstructed = jpeg_decompression(Y_padded, Y_q, Cb_q, Cr_q, Q,
downsamplingMode);
img_reconstructed = img_reconstructed(1:size(Y,1), 1:size(Y,2), :);% crop to
original
end
function [Qy, Qc] = getQuantMatrices(Q)
Qy_std = [
16 11 10 16 24 40 51 61;
12 12 14 19 26 58 60 55;
14 13 16 24 40 57 69 56;
14 17 22 29 51 87 80 62;
18 22 37 56 68 109 103 77;
24 35 55 64 81 104 113 92;
49 64 78 87 103 121 120 101;
72 92 95 98 112 100 103 99];
Qc_std = [
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];
if Q < 50
S = 5000 / Q;
else
S = 200 - 2 * Q;
end
Qy = round((S / 100) * Qy_std);
Qc = round((S / 100) * Qc_std);
end
function outBlock = jpegBlockDCT(blockStruct, Qmat)
block = double(blockStruct.data) - 128;
dct_block = dct2(block);
outBlock = round(dct_block ./ Qmat);
end
function outBlock = jpegBlockInverseDCT(blockStruct, Qmat)
block = double(blockStruct.data);
block = block .* Qmat;
idct_block = idct2(block);
outBlock = uint8(idct_block + 128);
end
function img_reconstructed = jpeg_decompression(~, Y_q, Cb_q, Cr_q, Q,
downsamplingMode)
[Qy, Qc] = getQuantMatrices(Q);
Y_dq = blockproc(Y_q, [8 8], @(b) jpegBlockInverseDCT(b, Qy));
Cb_dq = blockproc(Cb_q, [8 8], @(b) jpegBlockInverseDCT(b, Qc));
Cr_dq = blockproc(Cr_q, [8 8], @(b) jpegBlockInverseDCT(b, Qc));
switch downsamplingMode
case 'none'
case 'h'
Cb_dq = imresize(Cb_dq, [size(Cb_dq, 1), size(Cb_dq, 2) * 2],
'bilinear');
Cr_dq = imresize(Cr_dq, [size(Cr_dq, 1), size(Cr_dq, 2) * 2],
'bilinear');
case 'h+v'
switch downsamplingMode
case 'none'
case 'h'
Cb_dq = imresize(Cb_dq, [size(Cb_dq, 1), size(Cb_dq, 2) * 2],
'bilinear');
Cr_dq = imresize(Cr_dq, [size(Cr_dq, 1), size(Cr_dq, 2) * 2],
'bilinear');
case 'h+v'
Cb_dq = imresize(Cb_dq, [size(Cb_dq, 1) * 2, size(Cb_dq, 2) * 2],
'bilinear');
Cr_dq = imresize(Cr_dq, [size(Cr_dq, 1) * 2, size(Cr_dq, 2) * 2],
'bilinear');
otherwise
error('Invalid downsampling mode.');
In command window
rmse_vs_q('C:\Users\ahmed\Pictures\ph\2.jpg')