Return to Digital Photography Articles
How to extend a JPEG image losslessly
Interest in Lossless operations for JPEG photos
Over the last couple years, there has been great interest amongst photographers to preserve every detail of their original digital photos. It's widely known that resaving JPEG photos causes some degree of image degradation in the process. The amount of image degradation is typically very small, yet most serious enthusiasts cringe at the thought of any such "losses".
Interestingly, it would be hard to find an equivalent degree of image preservation in the context of film. Most of us accepted some uncertainty in the appearance of film grain itself. That said, recompression error in digital photos is more of an annoyance because it introduces some regularlities and inorganic boundaries (blockiness) into the resulting image.
Real Lossless Operations
There are some operations that one may perform on a digital photo that don't really "modify" the image content. Rotating, flipping and even cropping are operations that one often applies to digital photos, with the expectation that the image content will not suffer as a result. Thus, we have tools available to perform lossless rotation, lossless flip and lossless crop.
Since most people will fail to recognize a single round of high-quality recompression error in the resulting photograph, the desire for these lossless operations is generally based on the idea that you can perform the operation on the original file without saving a backup copy.
Should you decide later that you didn't like the outcome, you could simply losslessly rotate the image back to the original orientation, or flip. Of course cropping would be an irreversible operation.
Edited Lossless Operations
However, there has also been some interest in performing edits to a small region of the image content while still preserving the rest of the image losslessly. In this scenario, I would expect that users would in fact keep an original copy (prior to edit) and therefore the need for these edits to be "regionally" lossless is based primarily on concerns of image quality degradation for the new output file. As the recompression "error" resulting from one cycle of resaving with higher JPEG compression quality settings is quite minimal, this concern may generally be harder to justify.
With a few restrictions in mind, it is possible to make localized/regional edits to a JPEG image without affecting other regions. For example:
- Lossless extend / Lossless extension:
Increase the dimension of the JPEG image in one or more dimensions. The size of the extension in each direction must be a multiple of the MCU (usually x8, but x16 if chroma subsampling is used). For example, one can place a 16 pixel border around an digital photo, leaving the original content intact perfectly. In JPEGclub terminology, this operation is still called "lossless crop", even though it is a crop with negative dimensions. The JPEGclub page doesn't make this feature particularly obvious. - Lossless cut and paste / copy and paste:
Copy the content of one image into another, while preserving the content of the pasted photo portion and the underlying original image (outside of the paste region) perfectly. So long as both the source and destination box coordinates are aligned to the MCU boundaries, this can be done. This is a more complex operation, particularly when the two images have different quantization tables. In the JPEGclub terminology, this operation is called "lossless drop". - Local modification / edit / overlay:
Change only a portion of the original image content, leaving the rest intact perfectly. In this operation, only a region bounded by coordinates falling on the MCU boundaries will be touched, leaving all other MCU blocks untouched. If the coordinates of the region do not lie on MCU boundaries, then the size of the affected region is increased up to the next MCU boundary. This functionality is very similar to the lossless copy and paste, with the same difficulties when dealing with the quantization matrices.
Solutions
While I originally tried to accomplish these operations in the uncompressed image space (i.e. after JPEG decompression), I soon realized some of the limitations of the JPEG compression / decompression with regards to lossless reversibility. Therefore, the best way to accomplish this is by using a special utility that operates on images in the DCT block level, rather than at the uncompressed pixel level.
Downloading the Software: jpegtran
Most of these transformations can be performed with the sample programs provided by JPEGclub. The most relevant of these is a tool called jpegtran. jpegtran is a lossless transformation utility for JPEG images that can perform many different lossless operations on a digital photo. This includes lossless crop, lossless extend and lossless drop, etc.
While there are several Windows applications that provide this functionality, jpegtran is a free tool with source code provided, relying on the most recent jpeglib library version 6b. Unfortunately, command-line utilities are not particularly easy to use for the average Windows user, so it may be worth having a look at some of the fuller-featured (shareware, commercial and freeware) programs currently using this core functionality: lossless jpegtran software.
I will update this page once I find a more user-friendly front-end, free Windows application that has the lossless extend functionality. Note that while many applications provide the lossless crop capability (of jpegtran), very few seem to allow for the extend.
You can find the most recent versions of the free JPEG utilities on the JPEGclub website, or download versions from here:
jpegtran.zip (64KB, command line utility) Written by Thomas G. Lane
How do I do lossless extend?

Dimensions for Lossless Extend
First, one needs to determine the original image's dimensions. This is easily done with any image editor or even with Windows XP "Details" or "Properties->Summary". Let the width be Xo & the height be Yo.
How to create a border on all four sides: Let's assume that you have an original image with dimensions Xo x Yo pixels. You would like to add a border around the original image, with varying widths. Let Xl be the width of the extension to the left, Xr be the width of the extension to the right, Yt to the top and Yb to the bottom.
| Final Width | W = Xo + Xl + Xr |
|---|---|
| Final Height | H = Yo + Yt + Yb |
| Offset X | X = Xl |
| Offset Y | Y = Yl |
For example, to do a lossless extension on a JPEG image that is sized 3072 x 1024 pixels, with a top border of 8 pixels, left and right borders of 32 pixels and a bottom border of 40 pixels:
| Original Width | Xo = 3072 |
|---|---|
| Original Height | Yo = 1024 |
| Extension to Left | Xl = 32 |
| Extension to Right | Xr = 32 |
| Extension to Top | Yt = 8 |
| Extension to Bottom | Yb = 40 |
Therefore:
| Final Width | W = Xo + Xl + Xr | 3072+32+32 = 3136 |
|---|---|---|
| Final Height | H = Yo + Yt + Yb | 1024+8+40 = 1072 |
| Offset X | X = Xl | 32 |
| Offset Y | Y = Yl | 8 |
| COMMAND: | jpegtran -crop 3136x1072+32+8 before.jpg after.jpg | |
Assuming you have an original image named "before.jpg", typing in the above command into a shell window (command prompt) will generate a resulting image called "after.jpg".
How to Run jpegtran
If you are not familiar with command-line execution, please read the brief intro at the bottom of this page.
Restrictions on Size and Offsets
In general, all of the lossless transformation commands rely on the fact that the widths, heights as well as X & Y offsets are all multiples of the MCU size. In other words, all corners of the rectangles shown in the diagram above will line up on the boundaries of the JPEG MCU (Minimum Coded Unit) blocks.
In general, these blocks are 8x8 pixels, but can be 16x8, 16x16 or 8x16, depending on the chroma subsampling being used.
To be on the safe side, it is recommended that you try to select dimensions and offsets that are multiples of 16 pixels. In other words, W, H, X and Y should all be multiples of 16.
Command-line Execution of jpegtran
If you are not familiar with command-line execution, do the following:
- Download the jpegtran application, right-click on jpegtran.zip and select "Extract All...". Extract to the directory that contains the image you'd like to modify.
- Open up a command-line shell by selecting "Run..." from the Windows XP Start menu.
- Type in "cmd" and click on OK.
- Change directory into the folder you placed jpegtran (e.g. cd c:\photos\ )
- Make sure that both the photo and jpegtran are in your current directory (check with dir)
- Execute the command as described above (e.g. jpegtran -crop 3136x1072+32+8 before.jpg after.jpg )
Lossless Aspect Ratio Conversion Batch Script
One interesting use for the lossless extend capability is in converting images from the 4:3 aspect ratio (typical in point & shoot digicams) to the 3:2 aspect ratio (common in nearly all SLRs).
It is extremely easy to create a Windows batch script (.bat) that automatically performs a lossless extension of your JPEG images to match a new aspect ratio. While there are tools out there such as BetterJPEG that allow you to perform lossless operations, it is sometimes nice to use a free alternative that can perform the same operation across many files at the same time.
The following batch script was originally suggested by reader Michael Lee.
@echo off
for %%i in (%*) do jpegtran -copy all -crop 2916x1944+166+0 %%i "\%%~ni%%~xi"
The very last part ("\%%~ni%%~xi") will cause this script to place the converted output JPEG files in your root directory (e.g. C:\) with the same name as the original. As I prefer to keep original and derivate files distinguished by filename, I would offer an alternate last part which places the output files in the same directory as the originals, but with an -x suffix (note that this must be on the same line as the original for command):
"%%~di%%~pi%%~ni-x%%~xi"
The following table is an example of the parameters you can use to convert from a 4:3 image to a 3:2 image for common digicam resolutions. Substitute the parameter string in the script above (which reads 2916x...+0) for your particular camera and orientation as suggested below.
| Megapixels | Dimensions | Landscape | Portrait | ||
|---|---|---|---|---|---|
| 5 (5.04) | 2592x1944 | 2916x1944+162+0 | 1944x2916+0+162 | ||
| 4 (3.98) | 2304x1728 | 2592x1728+142+0 | 1728x2592+0+142 | ||
| 4 (3.87) | 2272x1704 | 2556x1704+142+0 | 1704x2556+0+142 | ||
| 3 (3.15) | 2048x1536 | 2304x1536+128+0 | 1536x2304+0+128 |
In the general case, the formula you can use to determine your new crop parameter string for a given digicam is:
Let orig_x be the horizontal resolution of your digicam
Let orig_y be the vertical resolution of your digicam
| For conversion of landscape photos: | For conversion of portrait photos: |
|---|---|
|
nx = 1.5 * orig_y ny = orig_y ox = ( (1.5 * orig_y) - orig_x) / 2 oy = 0 |
nx = orig_x ny = 1.5 * orig_x ox = 0 oy = ( (1.5 * orig_x) - orig_y) / 2 |
The parameter string will then be: nxxny+ox+oy
Modify the crop parameter string as noted above, matching the desired conversion for your digicam. Save this file as a batch file (e.g. jpegexth.bat for landscape and jpegextv.bat for portrait conversions) into the same directory as your jpegtran executable. Then, you can simply drag and drop multiple JPEG files (up to 9) onto the batch file. This will automatically generate output JPEG files as described in the NOTE section above. Note that you need to use a different batch script for landscape photos and portrait photos.
Installing Context Menu for Script
If this conversion is someone you intend to use often, you may want to consider installing the script in Windows explorer's right-click context menu. This way, you can simply right-click on the JPEG image to convert, and select the conversion type. There are several methods you can use to install right-click context menu option. For example, in Windows XP explorer, select Tools->Folder Options, then click on the File Types tab. From there, select the JPEG Image type and click on Advanced.
Click on New... to generate a new context-menu option, and enter a representative name for the action, such as Lossless Ext 3:2 Horz. Where it asks for the application, locate your batch file and append %1. Click OK and then you're done!

Reader's Comments:
Please leave your comments or suggestions below!The quantization table problem is a pity, but if I can check them first and default to lossy operation if they are not the same, that'll be good enough. Am I right in saying that all jpegs produced by the same software at the same settings will use the same quantization table?
Since I wrote that first comment I've been getting my hands dirty with a hex editor trying to see what I could do. Keeping it simple for now, I made two equal sized jpegs 128x128 with the same compression settings. Then I removed what I guessed was the image data from the second jpeg (I compared both files and presumed the block of data that was different was the image data, it came a line of bytes after the FFDA marker). I appended that data before the FFD9 marker of the first jpeg, changed the dimensions in the FFC0 marker and saved the new file.
The result was a viewable jpeg with the correct dimensions but although the data from jpeg A still displayed correctly, the jpeg B data seemed to have lost most of its colour information.
It was clearly never going to be that easy but do you know what step it is I'm missing out? Do I have to huffman decode all the image data and then join them together?
I guess once I get to appending jpegs on the x axis it's going to be whole different world of pain... bah.
This is usually the case. But if you are planning to blindly append scan image data together, you'll also have to ensure that the huffman tables are identical as well. The software may select a different set of huffman tables if "optimization" is turned on.
Your plan may only work when trying to append image data to the bottom of an existing image, not the side (as JPEG encodes in raster direction, left-to-right, top-to-bottom).
The trouble you are running into when you perform the appending is that the scan data is defined with variable-length codes that defined on bit-boundaries, not byte-boundaries. This means that you can't simply append another byte-stream. You need to a) determine precisely what bit alignment the last scan data ended and b) shift the newly-appended data stream the correct number of bits. Note that part a) is actually very difficult to do without processing the huffman codes.
Good luck!
1) Can you join two jpegs together if one of them has a dimension that is not a multiple of 16px. Ie join a 64x64 jpg to a 64x37 jpg?
Could you do it so that no recompression is applied to the image up until the last complete MCU boundary ie at 64+32=96px on the y axis. So the bottom 5px strip of the final image would have further encoding applied to it?!
I hope I'm making sense! I'm trying to make a little java program that joins jpgs together and aside from the quality considerations, if I don't have to decode images I can get huge memory savings (so the program can deal with much larger images).
As an aside, I just noticed you freedive too! That's a really special sport. i did a brief course a few years back in Thailand and absolutely loved it. I would go to a local bay, swim out for a few hundred metres and then dive down to the shallow bottom at about 10 metres or so. There I'd just lie down and chill out on the sandy floor in my underwater living room with that amazing shimmering sunlight effect on the surface of the water and the ripple wave patterns that shaped the sand. Never made it very deep, but that didn't really matter. Aaaahhhh... Good days.
Theoretically, you could join two JPEG images together "losslessly", despite having one with dimensions that are not a multiple of the MCU size. However, I don't know of any tools that would support this special case. The first step will be to change the image dimensions to be a full MCU (ie. 64x48). Then, one would have to ensure that the quantization tables used in both images are identical. Only then will it be possible.
If the image with the "partial MCU" is on the bottom (in your particular example), then after joining one can again update the image size dimensions to reflect the truncation (ie. from 64x112 to 64x101). It's important to consider that all JPEG images actually encode the contents of the full MCUs, but that it is only through the image dimension settings that we choose to truncate (effectively hide) the last few pixels.
The biggest challenge you may face with your java application will be that both input files will have to have the same quantization tables, otherwise you are not likely to be performing this operation losslessly.
As for the freediving, yes, it certainly is a wonderful thing to learn. I can only imagine what it must be like to use your skills in the fantastic waters off Thailand! Our local BC waters here are often dark & cold! (It may be a little hard to find rays of shimmering sunlight below!)
Regards
Martin
Regards
Martin