Working with JPEG

JPEG is a lossy compression algorithm based on imperfections of the human sense of vision. It allows adjusting degree of compression, making a compromise between file size and image quality. This article describes the JPEG compression algorithm in general and examines how to set up JPEG reading and writing settings in Graphics Mill. Also here you will learn how to remove alpha channel before saving to JPEG.

JPEG Algorithm

JPEG compression algorithm includes the following steps:

  1. color space conversion
  2. chroma subsampling
  3. segmentation into blocks
  4. discrete cosine transform
  5. quantization
  6. encoding

The decompression process includes all these steps in reverse order. The figure below demonstrates the JPEG compression-decompression cycle:

Color Space Conversion

On this step the RGB (Red, Green, Blue) image colors are converted to YCbCr (Luminance, Chrominance Blue, Chrominance Red) color channels. These three channels are typically less interdependent then RGB, which allows storing them with different resolution. If the image is CMYK or grayscale, it is processed without color conversion.

Chroma Subsampling

People's sense of vision is much more sensitive to changes in luminance than to changes in chrominance. This means that larger changes in the chrominance may be neglected without affecting image perception. Therefore, the color information can be stored at a lower resolution than the luminance information. This method can be applied only to images in the YCbCr color space. CMYK images use all the channels to store color information, and each color channel is compressed and quantized with the same quality. Grayscale images have no color information and do not need to be converted. Graphics Mill allows for changing chrominance resolution in compression options. For more information see the Enabling Chroma Subsampling section.

Segmentation into Blocks

On this step the image is divided into blocks, each block measuring 8 pixels long by 8 pixels wide. On subsequent steps each block is processed without reference to the others, so all actions are described relative to a single block. This feature provides an ability to scale the image in 2, 4, or 8 times while decoding. This is accomplished by processing several blocks and combining them in a single one. For example, the algorithm can iterate 16x16 pixels (four 8x8 blocks) to downscale the image by 2 times horizontally and vertically.

Discrete Cosine Transform

On this step each color component (for example, Y, Cb, and Cr for YCbCr) undergoes the Discrete Cosine Transform (DCT) process. The DCT is similar to the Fourier Transform, meaning that it produces a kind of spatial frequency spectrum.

Quantization

On this step the amplitudes of frequency components are quantized. Human vision is much more sensitive to small variations in color or brightness over large areas (low-frequency components) than to variations occurring on every pixel (high-frequency components). Therefore, high-frequency components are stored with a lower accuracy than low-frequency ones. The quality setting of the writer affects the resolution of each frequency component. For more information on how to set up JPEG compression quality in Graphics Mill see the Setting Up Quality section.

Encoding

The resulting data is compressed using a lossless algorithm, a variant of Huffman encoding.

Graphics Mill JPEG Writer Settings

Setting Up Quality

The quality option affects the image quality and the file size. Lower quality means smaller file size. The quality range is from 0 to 100, where the value 100 means no quantization. Although, the absence of quantization does not mean the absence of image data loss.

The value of this parameter depends on the image quality or the file size you want to obtain. You may decrease the setting value for high-resolution images that have few fine details in order to obtain less file size. For web images, which generally have a low resolution and contain some portions with fine details, it is better to increase the quality value. The recommended quality value is from 50 to 85.

Depending on the Graphics Mill API tier you use, you can utilize the JpegSettings.Quality property or the JpegWriter.Quality property to specify the image quality. The default quality value is 75.

Enabling Chroma Subsampling

Using 2x2 chroma subsampling produces good quality and compression results for high-resolution landscapes, portraits, and other not very detailed images. On the other hand, turning chroma subsampling off is more suitable for low-resolution web images with fine details, such as text over a uniform background or flat-color images.

Graphics Mill provides the JpegSettings.UseSubsampling and JpegWriter.UseSubsampling properties to specify whether to enable the subsampling during the compression. If 2x2 chroma subsampling is used, the image is divided into 2x2 pixel blocks and only the average color information for each block is stored. Graphics Mill applies chroma subsampling to RGB images only, which are automatically converted to YCrCb during the compression.

In Graphics Mill chroma subsampling is enabled by default.

Note

The fact that this method is based on the limitations of the human eye means that it is not a particularly good mechanism for compressing images which will be used by other "sensors". Especially, for "sensors" perceiving the chrominance and the luminance equally well.

Enabling Progressive JPEG

A simple JPEG file is stored as one top-to-bottom scan of the image, while the progressive JPEG divides the file into a series of scans. The first scan shows the image at a very low quality, and therefore it takes very little space. The following scans gradually improve the quality. Each scan is added to the data already provided, so that the total storage requirement is roughly the same as for a simple JPEG image of the same quality as the final scan.

The advantage of the progressive JPEG technique is that an image can be viewed on-the-fly as it is transmitted. The low-quality image version can be viewed very quickly, and the image quality is gradually improved as the image is loading. This looks much nicer than a slow top-to-bottom displaying of an image.

The progressive JPEG technique has the following disadvantages:

  • Each progressive JPEG scan takes about the same amount of computation to display as a whole simple JPEG file would.
  • The decoder requires a buffer for all the partial (and final) scans.

By default, progressive JPEG is disabled in Graphics Mill. To turn the feature on set the JpegSettings.IsProgressive or the JpegWriter.IsProgressive property to true.

Code Snippets

All code snippets here perform the same actions, but use different Graphics Mill API tiers. The first two snippets utilize the JpegSettings class. And the last two snippets use the JpegWriter class.

The following code saves JPEG file with the quality value set to 90, disabled chroma subsampling, and the enabled JPEG progressive technique. The code uses the JpegSettings class:

C#
using (var bitmap = new Bitmap(@"Images\in.jpg"))
{
    var jpegSettings = new JpegSettings();
    jpegSettings.Quality = 90;
    jpegSettings.UseSubsampling = false;
    jpegSettings.IsProgressive = true;
    bitmap.Save(@"Images\Output\out.jpg", jpegSettings);
}

The previous snippet can be rewritten in a simpler way by using the JpegSettings(Int32,Boolean,Boolean) constructor, which accepts quality, chroma subsampling, and progressive JPEG settings as arguments:

C#
using (var bitmap = new Bitmap(@"Images\in.jpg"))
{
    bitmap.Save(@"Images\Output\out.jpg", new JpegSettings(90, false, true));
}

The following code saves JPEG file with the quality value set to 90, disabled chroma subsampling, and the enabled JPEG progressive technique. The code uses the JpegWriter class:

C#
using (var reader = new JpegReader(@"Images\in.jpg"))
using (var writer = new JpegWriter(@"Images\Output\out.jpg"))
{
    writer.Quality = 90;
    writer.UseSubsampling = false;
    writer.IsProgressive = true;
    Pipeline.Run(reader + writer);
}

Or using the JpegWriter(String,Int32,Boolean,Boolean) constructor:

C#
using (var reader = new JpegReader(@"Images\in.jpg"))
using (var writer = new JpegWriter(@"Images\Output\out.jpg", 90, false, true))
{
    Pipeline.Run(reader + writer);
}

Graphics Mill JPEG Reader Settings

JpegReader supports a very important setting - Scale. This property allows generating a downscaled version of the original image while decoding it. It works several times faster than the resize operation. The limitation of this feature is that images can be scaled in 2, 4, or 8 times only.

The following code snippet uses the x4 scale value which "tells" the reader to return the image at 1/4 of the original width/height, and 1/16 the number of pixels.

C#
using (var reader = new JpegReader(@"Images\in.jpg"))
using (var writer = new JpegWriter(@"Images\Output\out.jpg", 90, false, true))
{
    reader.Scale = JpegScale.x4;
    Pipeline.Run(reader + writer);
}

Removing Alpha Channel

The JPEG format does not support alpha channels. If you export an image from a format which supports alpha transparency (for example, PNG) to JPEG, then you will get an UnsupportedPixelFormat exception. To avoid this situation, you should remove the alpha channel from the bitmap before saving this bitmap to JPEG. To perform this use the Bitmap.Channels collection and its RemoveAlpha method. The Bitmap.HasAlpha property allows checking to see if the bitmap contains the alpha channel.

The following code snippets load a PNG image, replace the alpha channel with white background, and save the result image in JPEG format. The first snippet uses the Bitmap.Save method to save the image. And the second one utilizes the JpegWriter class.

C#
using (var bitmap = new Bitmap(@"Images\watermark.png"))
{
    if (bitmap.HasAlpha)
        bitmap.Channels.RemoveAlpha(RgbColor.White);
    
    bitmap.Save(@"Images\Output\out.jpg");
}
C#
using (var reader = new PngReader(@"Images\watermark.png"))
using (var removeAlpha = new RemoveAlpha(RgbColor.White))
using (var writer = new JpegWriter(@"Images\Output\out.jpg"))
{
    Pipeline.Run(reader + removeAlpha + writer);
}

See Also

Reference

Manual