PCPlus 279: JPEG compression

I write a monthly column for PCPlus, a computer news-views-n-reviews magazine in the UK (actually there are 13 issues a year — there’s an Xmas issue as well — so it’s a bit more than monthly). The column is called Theory Workshop and appears in the Make It section of the magazine. When I signed up, my editor and the magazine were gracious enough to allow me to reprint the articles here after say a year or so. What I’ll do is publish the article from a year ago or so here when I purchase the current issue.

PCPlus logoOne of the topics I wanted to write up back when I had a two-page article was how JPEG compression worked, but I didn’t think I could cover it adequately in such a small space. So for March 2009 I tried with my new three-page allowance, but found that it was equally as difficult. Trouble is, there’s so much to talk about: colour spaces, DCTs, downsampling, Huffman encoding, and so on, so forth. So in the end, it turned into more of a layman’s discussion than any kind of deeper/broader article that laid down the foundations of why JPEG compression works, and why, sometimes, it doesn’t very well.

I also tried to show with an image, what the conversion of an RGB image to the YCbCr colour space would look like, and completely ignored the fact that, although our screens use the RGB colour space, printers use the CYMK colour space. I was expecting it all to get translated from RGB to CYMK properly and the inks to cooperate as they were laid onto paper, etc. Even looking at the PDF of the article, that “decomposition” image looks weird. This is what it should look like (click on it for the full size image):

RGB to YCbCr conversion

I actually created each supplementary image in code by using the RGB-YCbCr conversion equations on the original image and then stitched them together.

    private void button1_Click(object sender, EventArgs e) {
      Bitmap input = new Bitmap(@"D:\Users\Julian M Bucknall\Pictures\Group.jpg");

      Bitmap lumaImage = new Bitmap(input.Width, input.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
      Bitmap crImage = new Bitmap(input.Width, input.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
      Bitmap cbImage = new Bitmap(input.Width, input.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

      for (int c = 0; c < input.Width; c++) {
        for (int r = 0; r < input.Height; r++) {
          Color color = input.GetPixel(c, r);
          int Y  = (int) Math.Round(( 0.2990 * color.R) + (0.5870 * color.G) + (0.1140 * color.B));
          int Cb = (int) Math.Round((-0.1687 * color.R) - (0.3313 * color.G) + (0.5000 * color.B));
          int Cr = (int) Math.Round(( 0.5000 * color.R) - (0.4187 * color.G) - (0.0813 * color.B));
          color = Color.FromArgb(Y, Y, Y);
          lumaImage.SetPixel(c, r, color);
          Cr *= 2;
          if (Cr >= 0)
            color = Color.FromArgb(Cr, 0, 0);
          else
            color = Color.FromArgb(0, -Cr, -Cr);
          crImage.SetPixel(c, r, color);
          Cb *= 2;
          if (Cb >= 0)
            color = Color.FromArgb(0, 0, Cb);
          else
            color = Color.FromArgb(-Cb, -Cb, 0);
          cbImage.SetPixel(c, r, color);
        }
      }
      lumaImage.Save(@"D:\Users\Julian M Bucknall\Pictures\GroupLuma.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
      crImage.Save(@"D:\Users\Julian M Bucknall\Pictures\GroupCr.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
      cbImage.Save(@"D:\Users\Julian M Bucknall\Pictures\GroupCb.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
    }

Well, OK, there is a little bit of futzing around in there so that you can more easily visualize the Cr and Cb images. I can’t remember now why I chose the code I did; only that there was quite a bit of experimentation behind it so that the results would look good when printed, albeit inaccurate.

Although I’m not 100% happy with how the article turned out when printed, and possibly three pages is still too few to do the subject justice, I did enjoy the research and the playing around that went into it. It is a fascinating topic; one that has more than its fair share of voodoo (where do those constants in the RGB-YCbCr conversion equations come from again?).

This article first appeared in issue 279, March 2009.

You can download the PDF here.

(Quick aside: PCPlus used to put part of their archive as PDFs on the DVD in the back of the magazine. They’ve now moved to a CD instead of a DVD, presumably to save on costs, and the archive is no longer on there. I hear they’re going to publish it online instead, sometime in the near future.)

Loading similar posts...   Loading links to posts on similar topics...

No Responses

Feel free to add a comment...

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response