Project 5: JPEG Compression

by Jay Wilson

Background

Image compression is made possible using the concept of orthogonality. Using a process called the discrete cosine transform, one can carry out a least squares approximation on an image, thus reducing the number of bits needed to store it. This process is known as lossy compression, and therefore results in some loss of clarity, but it is generally kept to a threshold that is either imperceptible or considered tolerable to the human eye.

Considering this project involved JPEG compression, I figured this was a great opportunity to play with an image of one of my favorite artists, JPEGMAFIA. (JPEG, or Peggy, for short.)

Black and White

Problem 3 called for manipulating a black and white image, leaving manipulation of color images for later problems. First, I read in the the file using imread and converted it to double type for increased accuracy. Next, I found an 8x8 square to use for initial testing. One speed bump I ran into was initially choosing a square that had too little going on. It was mostly dark-colored squares, so compressing it yielded an almost totally black image with little progression between different levels of compression. I found that a square in the patch of grass on JPEG's left worked best.

1234567% Problem 3
x = imread('peggy_bw.jpeg');
x=double(x);
%% part a
xb=x(500:507,600:607)
figure(1)
imagesc(xb);colormap(gray)

Next, I built the CC matrix and applied the 2D-DCT to the 8x8 section of the image and stored it as the matrix YY. Using this YY, I quantized the image using p=1p=1, dequantized the image, then applied the reverse 2D-DCT to reconstruct the image. I repeated this process for pp values of 2 and 4.

1234567891011121314151617181920212223%% part b: apply the 2d-dct
n=8
for i=1:n
  for j=1:n
    C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
  end
end
C=sqrt(2/n)*C;
C(1,:)=C(1,:)/sqrt(2);
xb = xb-128;
Y=C*xb*C';
%% parts c and d
% p=1
p=1; % changed to 2 and 4, respectively, for other two trials
Q=p*8./hilb(8);
Yq=round(Y./Q)
% dequantization step
Ydq=Yq.*Q;
Xdq=C'*Ydq*C; % inverse dct
Xe=Xdq+128;
Xf=uint8(Xe);
figure(2)
imagesc(Xf);colormap(gray)

Once I did that for the single 8x8 block, I repeated the process for the whole image.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657%% part e
p1mat = zeros(640,640);
p2mat = zeros(640,640);
p4mat = zeros(640,640);
for row=1:8:639
  for col=1:8:639
    xb=x(row:row+7,col:col+7);
     %% part b: apply the 2d-dct
    n=8;
    for i=1:n
      for j=1:n
        C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
      end
    end
    C=sqrt(2/n)*C;
    C(1,:)=C(1,:)/sqrt(2);
    xb = xb-128;
    Y=C*xb*C';
    %% parts c and d
    % p=1
    p=1;
    Q=p*8./hilb(8);
    Yq=round(Y./Q);
    % dequantization step
    Ydq=Yq.*Q;
    Xdq=C'*Ydq*C; % inverse dct
    Xe=Xdq+128;
    Xf=uint8(Xe);
    p1mat(row:row+7,col:col+7)=Xf;
    % p=2
    p=2;
    Q=p*8./hilb(8);
    Yq=round(Y./Q);
    % dequantization step
    Ydq=Yq.*Q;
    Xdq=C'*Ydq*C; % inverse dct
    Xe=Xdq+128;
    Xf=uint8(Xe);
    p2mat(row:row+7,col:col+7)=Xf;
    % p=4
    p=4;
    Q=p*8./hilb(8);
    Yq=round(Y./Q);
    % dequantization step
    Ydq=Yq.*Q;
    Xdq=C'*Ydq*C; % inverse dct
    Xe=Xdq+128;
    Xf=uint8(Xe);
    p4mat(row:row+7,col:col+7)=Xf;
  end
end
figure(1)
imagesc(p1mat);colormap(gray)
figure(2)
imagesc(p2mat);colormap(gray)
figure(3)
imagesc(p4mat);colormap(gray)

Problem 4 called for essentially the same process as Problem 3 but using the JPEG-suggested matrix for quantizing and using p=1p=1. Put another way, I did the same thing as Part E in the last problem, except with a constant matrix QQ instead of computing it during the quantization phase.

1234567891011121314151617181920212223242526272829303132333435363738% Problem 4
Q = [
      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;
    ];
p1mat = zeros(640,640);
for row=1:8:639
  for col=1:8:639
    xb=x(row:row+7,col:col+7);
    n=8;
    for i=1:n
      for j=1:n
        C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
      end
    end
    C=sqrt(2/n)*C;
    C(1,:)=C(1,:)/sqrt(2);
    xb = xb-128;
    Y=C*xb*C';
    % p=1
    p=1;
    Yq=round(Y./Q);
    % dequantization step
    Ydq=Yq.*Q;
    Xdq=C'*Ydq*C; % inverse dct
    Xe=Xdq+128;
    Xf=uint8(Xe);
    p1mat(row:row+7,col:col+7)=Xf;
  end
end
figure(4)
imagesc(p1mat);colormap(gray)

RGB

Problem 5 called for the same steps as Problem 3 except with a color image instead of a black and white one. While the process is much the same, using RGB instead of a simple black/white value means I had to process the R, G, and B values individually and recombine them into a single image in the end.

First, like in Problem 3, I imported the image and focused on an 8x8 square. This time, however, I also had to separate the imported matrix into separate R, G, and B matrices.

12345x=imread('peggy.jpeg');
x=double(x);
%% part a
xb=x(500:507,600:607,:);
r=xb(:,:,1);g=xb(:,:,2);b=xb(:,:,3);

Next, the process was again much the same as in Problem 3, just that the process done for just xb before then had to be done for R, G, and B.

1234567891011121314151617181920212223242526272829303132333435363738394041%% part b: apply the 2d-dct
n=8
for i=1:n
  for j=1:n
    C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
  end
end
C=sqrt(2/n)*C;
C(1,:)=C(1,:)/sqrt(2);
r = r-128;
g = g-128;
b = b-128;
Yr=C*r*C'
Yg=C*g*C'
Yb=C*b*C'
%% parts c and d
% p=1
p=1;
Q=p*8./hilb(8);
Yqr=round(Yr./Q)
Yqg=round(Yg./Q)
Yqb=round(Yb./Q)
% dequantization step
Ydqr=Yqr.*Q;
Ydqg=Yqg.*Q;
Ydqb=Yqb.*Q;
Xdqr=C'*Ydqr*C; % inverse dct
Xdqg=C'*Ydqg*C; % inverse dct
Xdqb=C'*Ydqb*C; % inverse dct
Xer=Xdqr+128;
Xeg=Xdqg+128;
Xeb=Xdqb+128;
Xfr=uint8(Xer);
Xfg=uint8(Xeg);
Xfb=uint8(Xeb);
figure(4)
imagesc(Xfr);colormap(autumn)
figure(5)
imagesc(Xfg);colormap(summer)
figure(6)
imagesc(Xfb);colormap(winter)

Finally, like before, I extended this process to the entire image.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677  %% part e
  rmat = zeros(640,640);
  gmat = zeros(640,640);
  bmat = zeros(640,640);
  for row=1:8:639
    for col=1:8:639
      xb=x(row:row+7,col:col+7,:);
      r=xb(:,:,1);g=xb(:,:,2);b=xb(:,:,3);
      %% part b: apply the 2d-dct
      n=8;
      for i=1:n
        for j=1:n
          C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
        end
      end
      C=sqrt(2/n)*C;
      C(1,:)=C(1,:)/sqrt(2);
      r = r-128;
      g = g-128;
      b = b-128;
      Yr=C*r*C';
      Yg=C*g*C';
      Yb=C*b*C';
      %% parts c and d
      % p=1
      p=8;
      Q=p*8./hilb(8);
      Yqr=round(Yr./Q)
      Yqg=round(Yg./Q)
      Yqb=round(Yb./Q)
      % dequantization step
      Ydqr=Yqr.*Q;
      Ydqg=Yqg.*Q;
      Ydqb=Yqb.*Q;
      Xdqr=C'*Ydqr*C; % inverse dct
      Xdqg=C'*Ydqg*C; % inverse dct
      Xdqb=C'*Ydqb*C; % inverse dct
      Xer=Xdqr+128;
      Xeg=Xdqg+128;
      Xeb=Xdqb+128;
      Xfr=uint8(Xer);
      Xfg=uint8(Xeg);
      Xfb=uint8(Xeb);
      rmat(row:row+7,col:col+7)=Xfr;
      gmat(row:row+7,col:col+7)=Xfg;
      bmat(row:row+7,col:col+7)=Xfb;
    end
  end
  % rgb images
  r=x(:,:,1);g=x(:,:,2);b=x(:,:,3);
%  figure(1)
%  imagesc(r);colormap(autumn)
%  figure(2)
%  imagesc(g);colormap(summer)
%  figure(3)
%  imagesc(b);colormap(winter)
%  figure(4)
%  imagesc(rmat);colormap(autumn)
%  figure(5)
%  imagesc(gmat);colormap(summer)
%  figure(6)
%  imagesc(bmat);colormap(winter)
  % complete images
  rgb=zeros(640,640,3);
  rgb(:,:,1)=r;
  rgb(:,:,2)=g;
  rgb(:,:,3)=b;
  rgb=uint8(rgb);
  figure(7)
  imagesc(rgb);
  rgbmat=zeros(640,640,3);
  rgbmat(:,:,1)=rmat;
  rgbmat(:,:,2)=gmat;
  rgbmat(:,:,3)=bmat;
  rgbmat=uint8(rgbmat);
  figure(8)
  imagesc(rgbmat);

YUV

Problem 6 calls for much the same process as in Problem 5, except using YUV instead of RGB. Rather than referring to different levels of colors, YUV instead refers to luminance and color difference. Luminance is defined as Y=0.299R+0.587G+0.114BY = 0.299R+0.587G+0.114B, and the color differences are U=BYU=B-Y and V=RYV=R-Y. I used these values just as I used the RGB values in the last problem. In the end, however, I had to convert them back to RGB to get the proper image.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139  % Problem 6
  x=imread('peggy.jpeg');
  x=double(x);
  %% part a
  xb=x(500:507,600:607,:);
  r=xb(:,:,1);g=xb(:,:,2);b=xb(:,:,3);
  y=0.299*r+0.587*g+0.114*b;
  u=b-y;
  v=r-y;
%  figure(1)
%  imagesc(y);
%  figure(2)
%  imagesc(u);
%  figure(3)
%  imagesc(v);
  %% part b: apply the 2d-dct
  n=8
  for i=1:n
    for j=1:n
      C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
    end
  end
  C=sqrt(2/n)*C;
  C(1,:)=C(1,:)/sqrt(2);
  y = y-128;
  u = u-128;
  v = v-128;
  Yy=C*y*C'
  Yu=C*u*C'
  Yv=C*v*C'
  %% parts c and d
  % p=1
  p=1;
  Q=p*8./hilb(8);
  Yqy=round(Yy./Q)
  Yqu=round(Yu./Q)
  Yqv=round(Yv./Q)
  % dequantization step
  Ydqy=Yqy.*Q;
  Ydqu=Yqu.*Q;
  Ydqv=Yqv.*Q;
  Xdqy=C'*Ydqy*C; % inverse dct
  Xdqu=C'*Ydqu*C; % inverse dct
  Xdqv=C'*Ydqv*C; % inverse dct
  Xey=Xdqy+128;
  Xeu=Xdqu+128;
  Xev=Xdqv+128;
  Xfy=uint8(Xey);
  Xfu=uint8(Xeu);
  Xfv=uint8(Xev);
  figure(4)
  imagesc(Xfy);
  figure(5)
  imagesc(Xfu);
  figure(6)
  imagesc(Xfv);
  %% part e
  ymat = zeros(640,640);
  umat = zeros(640,640);
  vmat = zeros(640,640);
  for row=1:8:639
    for col=1:8:639
      xb=x(row:row+7,col:col+7,:);
      r=xb(:,:,1);g=xb(:,:,2);b=xb(:,:,3);
			y=0.299*r+0.587*g+0.114*b;
			u=b-y;
			v=r-y;
      %% part b: apply the 2d-dct
      n=8;
      for i=1:n
        for j=1:n
          C(i,j)=cos((i-1)*(2*j-1)*pi/(2*n));
        end
      end
      C=sqrt(2/n)*C;
      C(1,:)=C(1,:)/sqrt(2);
      y = y-128;
      u = u-128;
      v = v-128;
      Yy=C*y*C';
      Yu=C*u*C';
      Yv=C*v*C';
      %% parts c and d
      % p=1
      p=8;
      Q=p*8./hilb(8);
      Yqy=round(Yy./Q)
      Yqu=round(Yu./Q)
      Yqv=round(Yv./Q)
      % dequantization step
      Ydqy=Yqy.*Q;
      Ydqu=Yqu.*Q;
      Ydqv=Yqv.*Q;
      Xdqy=C'*Ydqy*C; % inverse dct
      Xdqu=C'*Ydqu*C; % inverse dct
      Xdqv=C'*Ydqv*C; % inverse dct
      Xey=Xdqy+128;
      Xeu=Xdqu+128;
      Xev=Xdqv+128;
      Xfy=uint8(Xey);
      Xfu=uint8(Xeu);
      Xfv=uint8(Xev);
      ymat(row:row+7,col:col+7)=Xfy;
      umat(row:row+7,col:col+7)=Xfu;
      vmat(row:row+7,col:col+7)=Xfv;
    end
  end
  % rgb images
  r=x(:,:,1);g=x(:,:,2);b=x(:,:,3);
  y=0.299*r+0.587*g+0.114*b;
  u=b-y;
  v=r-y;
%  figure(1)
%  imagesc(y);
%  figure(2)
%  imagesc(u);
%  figure(3)
%  imagesc(v);
%  figure(4)
%  imagesc(ymat);
%  figure(5)
%  imagesc(umat);
%  figure(6)
%  imagesc(vmat);
  % complete images
  rgb=zeros(640,640,3);
  rgb(:,:,1)=v+y;
  rgb(:,:,2)=(y-0.299*r-0.114*b)/(0.587);
  rgb(:,:,3)=u+y;
  rgb=uint8(rgb);
  figure(7)
  imagesc(rgb);
  rgbmat=zeros(640,640,3);
  rgbmat(:,:,1)=vmat+ymat;
  rgbmat(:,:,2)=(ymat-0.299*r-0.114*b)/(0.587);
  rgbmat(:,:,3)=umat+ymat;
  rgbmat=uint8(rgbmat);
  figure(8)
  imagesc(rgbmat);

fun time

Displaying the YUV directly:

The YUV compressed using p=8p=8:

hack the world