Return to Digital Photography Articles
Lossless Rotation, lossless crop - How to test?
With all of the concern about lossless rotation, it's worth knowing whether or not your particular software is capable of rotation without incurring additional compression error. It is virtually impossible to do this visually, and so a more object means must be used to perform this check. Fortunately, it is very easy to do, and I describe two of these methods below.
Lossless rotation test procedure
- Duplicate an original JPEG image (eg. test.jpg copied to test-rotated.jpg)
- Open the copied version (test-rotated.jpg)
- Perform a 90 degree rotation and then resave it as test-rotated.jpg (if it isn't done for you automatically)
- Close the image
- Open the rotated version (test-rotated.jpg)
- Do three consecutive 90 degree rotations (in the same direction as the original), which should return the image back to the original orientation. Resave it again as test-rotated.jpg.
- Verify that the rotation between test.jpg and test-rotated.jpg is lossless using one of the described listed below:
|Original vs Consecutive Rotated|
Method A: Image Subtraction
The following procedure uses Photoshop, but many other graphics software packages can perform a similar operation. The idea behind this method is to get Photoshop to compute the mathematical difference in RGB values on every pixel between the original and the rotated versions. If there are any differences, use one of two methods to help make these differences easier to visualize.
- Open up the original image (test.jpg)
- Open up the fully-rotated image (test-rotated.jpg)
- While holding down the shift key, drag the thumbnail of the fully-rotated image from the layer's palette to the original image. This should create a new layer in the original file's window. The shift key ensures that the new layer is centered properly above the original layer. The layer, "Background" contains test.jpg, and the layer "Layer 1" will now contain test-rotated.jpg.
- With Layer 1 selected, change the layer mode to "Difference". The image window should go completely black as it is now displaying the mathematical difference between the two images. If there are any differences, they will probably be extremely hard to see. The next two steps will increase their visibility.
- Under the Layer menu (or Layer Palette drop-menu), select "Merge Visible". This will cause the calculated difference between the two layers to replace the original Background layer.
- Perform the objective comparison through either of the two visualization methods shown below (ie. either through the use of the Threshold command or the data in the Histogram window).
Under the Image menu, select "Threshold...". In the dialog box that comes up, enter a threshold value of 1 (the default was 128). This should cause your black difference image to either stay black or show regions of white. Any pixels that are black will represent areas that were untouched in the rotation process (ie. losslessly rotated). If any white pixels exist (or even the entire image), then the image was not rotated losslessly (ie. incremental compression error was added to the image).
Alternatively, instead of using the Threshold... command, one can simply examine the histogram for the difference image. Make sure that the histogram is shown (under the Window->Histogram). Make sure that the histogram is in Expanded View not Compact View (selected by the Histogram palette drop-down menu) and that Show Statistics is also checked. If a little alert triangle is showing in the top-right of the histogram window, click on it. This causes the histogram to be calculated from every pixel in the image, rather than a small sample (the default for speed reasons).
|Histogram of original image|
In the window, one will see a graph that represents the number of pixels at each intensity level, from 0 (far left) to 255 (far right). As this is based on the result of the difference function, a lossless rotation should have all pixels fall into the vertical slice on the far left (ie. 0 numerical RGB differences between the before and after images). While this method is easy to spot differences quickly, it's easy to miss some pixels (as the vertical axis is highly compressed and will not show any marks if only a small number of pixels were different). The next step describes a more accurate measure to ensure accuracy in the analysis.
|Lossless Rotation||Non-lossless Rotation|
Below the histogram are the statistics. For the purposes of this test, the values should show a Mean of 0.00, a Std Dev of 0.00, Median of 0, and a Percentile of 100.00 when the cursor is over the first column (when Level is shown as 0). For any other cursor positions within the histogram window (ie. when Level shows a value in the range 1..255), the value for Count should be 0.
Method B: File Compare
If you really want to be sure that the two images are identical, an alternative method is to compare the JPEG filestreams. This method ensures that the actual image content is preserved identically, irrespective of any changes to the EXIF information, maker notes, timestamps, ICC profiles, thumbnails, etc.
NOTE: The file-compare will report a false negative (mismatch when they are in fact identical) in cases where one of the JPEG images has been saved with optimized huffman coding tables. Therefore, for the purposes of your test, you should ensure that optimization is turned off during the lossless rotation process. Even though the file content changes during optimization, the image content represented by the compressed datastream is the same.
The following method is complex and only really recommended for those who have an unsatiable curiosity to see how things work.
Note that one cannot simply compare file sizes as the file size can change even though a rotation was lossless. What we want to ensure is that the image data is the same, and ignore all of the other information contained within the JPEG file.
A JPEG file (as specified in the JFIF, JPEG File Interchange Format) contains a number of elements (metadata), that can include:
- Thumbnail - smaller version of the photo for quick previews
- EXIF Details, such as time of capture, what focal length was used, etc.
- Comments (not often used)
- ICC Profiles - define the color management
- Maker Notes - specific to digital camera manufacturers; might indicate metering modes, etc.
- Image Data - The actual compressed image
In the following, we are only interested in comparing the last item in the list: the compressed image data. A comparison is made between an original image and one that has been doubly-rotated. The doubly-rotated version can be created by performing a 90 degree clockwise rotation, resaving, closing, reopening, rotating in the opposite direction (either 90 degrees counter-clockwise or 270 degrees clockwise) and then resaving. The original and doubly-rotated version should hopefully be identical.
- Extract the JPEG markers in the original
Using a utility such as jpegdump (by Kurt Stege) or JPEGsnoop (by me), locate all of the markers in the file. These JPEG markers all start with hex 0xFF. The jpegdump utility prints out each marker along with a starting offset and length.
- Locate the JPEG Start of Scan (SOS) for the main image in the original
Look for the JPEG marker 0xFFDA, which represents the start of the scan (the image data). You might see two of these within a photo: one for the reduced-resolution thumbnail and the other for the main image data. By comparing the marker section lengths, it should be clear which one is the main image data. The thumbnails are generally only a few kilobytes while the main image data might be a megabyte or more. It seems that most JPEG images include the image data's Start of Scan marker at the end of the file, so try checking the end of the marker list.
- Record the hex offset and length of the SOS segment in the original
Once you have located the marker for the main image data, record the hexadecimal file offset and decimal length of this marker segment.
- Repeat the above three steps for the doubly-rotated image
- Dump the hex data corresponding to the two segments
Using a utility such as dumphex (by Robert Bachmann, download here), convert the binary bytestream from the SOS segment into a text file. Note that you can specify the starting file offset in hex and the length in decimal quantities.
- Using a file compare to verify equivalence between the two hex dumps
Using a utility such as the Data Viewer within Beyond Compare (by Scooter Software), compare the two hex dumps side-by-side. Within Beyond Compare, one can configure the Data Viewer to treat the text files as column-formatted by indicating whitespace as a delimiter. This way we can later ignore the hex file offset that starts at the beginning of each line. When one sees the preview of how the delimiting works, one can select the first column (which is the hex file offset) and mark it as Removed. Once the files are then compared, the result of Files Match will be shown if the images were losslessly rotated!
Given the steps above, extracting the JPEG markers:
jpegdump.exe "e:\photos\original2.jpg" . . . . Marker FFDA at file offset 00005E62: (SOS) Start of Scan field SOS has len 12 hopefully skipping 758526 bytes of compressed image data to next 0xFF.
Dumping the hex code of the SOS segment:
dumphex.exe /s05e62 /L758526 /nc /ooriginal2_hex.txt "e:\photos\original2.jpg"
The resulting hex file will appear like this:
00005E62h: FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 E5 48 00005E72h: F9 6B D8 52 3C BB 8D 1D 7D 2A 9B 27 61 7B F1 45 00005E82h: C7 7B 83 03 8A 84 C3 94 53 9C 60 D5 36 4B 41 93 00005E92h: 48 96 20 3B 79 A4 5A 62 9E D9 ED 55 B0 34 28 04 . . .
Note the Start of Scan marker (SOS) that is indicated by the hexadecimal string 0xFFDA.
In Beyond Compare, select the two hex dump text files (original2_hex.txt and original2_rot2_hex.txt) and launch Compare Using -> Data Viewer. This will prompt me to configure the Left and Right sides of the input. I select Whitespace under Delimiter and turn off First line is heading.
Setting the Beyond Compare options
Under Data Compare Rules, I then change the Column Handling for Column 1 to Removed.
Ignore the hex file offset
If the image was doubly rotated losslessly, it will report Files match at the bottom of the window.
The final compare result!