Applying Lossless JPEG Transforms

Processing JPEG files may result in image quality loss. The reason for this is the lossy nature of the JPEG compression algorithm. If you perform open/save operations on a JPEG file several times, then the image quality degrades because of JPEG artifacts superposition.

Graphics Mill allows skipping the decoding-encoding step for certain JPEG-processing operations and applies them directly on compressed JPEG data. This topic describes which JPEG operations can be performed without quality loss and how to perform them.

The LosslessJpeg class encapsulates all functionality related to lossless JPEG transformations in Graphics Mill. To use this class you should perform at least two steps:

  1. Open a JPEG file using LosslessJpeg constructors.
  2. Call a LosslessJpeg.WriteXXX method correspondingly to the operation you want to perform.

Lossless Rotate and Flip

The simplest lossless operation on a JPEG image is a 90-degree rotation. To rotate a JPEG image, use the LosslessJpeg.WriteRotated method as shown in the following snippet:

C#
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg"))
{
    losslessJpeg.WriteRotated(@"Images\Output\out.jpg", System.Drawing.RotateFlipType.Rotate90FlipNone);
}

The LosslessJpeg.WriteRotated method allows not only rotating a JPEG image, but also flipping it horizontally or vertically. To flip an image pass the appropriate RotateFlipType value as a method parameter, like follows:

C#
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg"))
{
    losslessJpeg.WriteRotated(@"Images\out.jpg", System.Drawing.RotateFlipType.RotateNoneFlipX);
}

According to the JPEG specification any JPEG image is built of blocks of pixels called MCUs (Minimum Coded Units). Typically MCU size is a number divisible by 8. Lossless rotation or flip rearranges MCU blocks and pixels of each MCU in a new order. If an image width or height is non-divisible by MCU size, then lossless rotation/flip will crop the image. You can use the LosslessJpeg.IsTrimmingRequired(RotateFlipType) method to check if the image will be cropped. The method returns true if the image will be cropped in the result of a specified lossless operation.

Lossless Crop

Lossless crop offers benefits not only in terms of the resulting image quality, but also in terms of CPU and memory usage. You save CPU resources by skipping JPEG decompression and do not waste memory on pixels which will be cropped out.

The following code demonstrates how to use the LosslessJpeg.WriteCropped method:

C#
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg"))
{
    losslessJpeg.WriteCropped(@"Images\Output\out.jpg", new System.Drawing.Rectangle(64, 40, 157, 117));
}

Partial JPEG Recompression

The LosslessJpeg.WritePatched method allows applying any Graphics Mill effect on a part of a JPEG image without recompression of the entire image. It is useful when changing only a small part of an image, for example, adding a watermark, removing red eyes, etc.

One of the widely used tasks where patching can be applied is hiding license plate numbers when publishing car photos in the web. The idea is as follows:

  1. Crop a license plate from a car photo.
  2. Apply the mosaic effect on the cropped image.
  3. Patch the original image with the image get on the previous step.

The patched car photo will look like the following one:

Result image.

The following code replaces a part of an image with its mosaic-transformed version:

C#
var rect = new System.Drawing.Rectangle(152, 136, 72, 32);

using (var patchBitmap = new Bitmap())
{
    //Apply crop and mosaic transfroms
    using (var input = new Bitmap(@"Images\in.jpg"))
    using (var crop = new Crop(rect))
    using (var mosaic = new Mosaic(4, 4))
    {
        Pipeline.Run(input + crop + mosaic + patchBitmap);
    }

    //Patch JPEG
    using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg"))
    {
        rect = losslessJpeg.AlignToMCUSize(rect, JpegAlignToSampleSizeMode.Patch);
        losslessJpeg.WritePatched(@"Images\Output\out.jpg", rect.Location, patchBitmap);
    }
}
Note

The WritePatched method cannot draw a bitmap with arbitrary coordinates. The coordinates must be aligned to MCU size. Alignment can be easily performed by using the LosslessJpeg.AlignToMCUSize method, which takes given coordinates and returns them aligned to the JPEG sample size.

Updating Metadata Without Recompression

Processing JPEG files is not always meant to change JPEG image, sometimes you only need to modify the metadata. To perform this task without JPEG recompression you can use the following properties of the LosslessJpeg class:

After you update metadata, call the LosslessJpeg.Write method to apply the changes.

The following code updates fields of all four metadata types:

C#
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg"))
{
    // IPTC
    var iptc = losslessJpeg.Iptc;
    if (iptc == null)
        iptc = new IptcDictionary();
    iptc[IptcDictionary.Caption] = "Mountain";

    // EXIF
    var exif = losslessJpeg.Exif;
    if (exif == null)
        exif = new ExifDictionary();
    exif[ExifDictionary.Software] = "Aurigma Graphics Mill";

    // XMP
    var xmp = new XmpData();
    if (losslessJpeg.Xmp != null)
        xmp.Load(losslessJpeg.Xmp);
    var node = new XmpValueNode(XmpNodeType.SimpleProperty, "John Doe", XmpTagNames.DCCreator);
    xmp.AddNode(node);
    losslessJpeg.Xmp = xmp.Save();

    // Adobe image resource blocks
    var adobeResources = losslessJpeg.AdobeResources;
    if (adobeResources == null)
        adobeResources = new AdobeResourceDictionary();
    var arBlock = new AdobeResourceBlock("Copyright", new byte[] { 1 });
    adobeResources[0x040A] = arBlock;

    losslessJpeg.Write(@"Images\Output\out.jpg");
}

See Also

Reference

Manual