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.


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: (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 WidthW = Xo + Xl + Xr
Final HeightH = Yo + Yt + Yb
Offset XX = Xl
Offset YY = 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 WidthXo = 3072
Original HeightYo = 1024
Extension to LeftXl = 32
Extension to RightXr = 32
Extension to TopYt = 8
Extension to BottomYb = 40


Final WidthW = Xo + Xl + Xr 3072+32+32 = 3136
Final HeightH = Yo + Yt + Yb 1024+8+40 = 1072
Offset XX = Xl 32
Offset YY = 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 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):


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.

MegapixelsDimensions   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!

Fascinating stuff, as I've recently gotten back into imaged editing/processing. I have noted, that the most recent version of JpegTran does not seem to support this behavior. I've been scouring the net looking for a copy of version 6b, and I was wondering if you've still got one kicking around?
Conversely, if you've managed to get the most recent version (9) to extend, I'd love to here how you managed it.

 I'm having a problem somewhat similar to James.
I'm attempting to combine several very large (pixelwise) jpeg images. Jpegjoin continually fails without returning any error or even acknowledging that the operation could not be completed. I presume this is related to the sheer amount of blocks involved in the operation.

When GUI's fall short I'll resort to command line, but I can't find any example code to figure out how to perform jpegjoin's operations directly with jpegtran from command.

I've tried looking over the various jpegtran commands, but nothing seems to address concatenation or "drop"

On a side note, I often use a hex editor to extend the render boundaries to the next MCU in cases where the space is so insignificant that any color "stretching" would be unnoticed or the edge blocks have solid color which extend perfectly.

At first I started by creating solid black jpegs of the original and extended dimensions and saving them with the same chroma subsampling, comparing the difference in a hex editor and then using Jpegsnoop to locate the offset for the image to be modified and then making the alteration.

After awhile I realized I could forgo the time spent making black comparisons and looking up offsets in Jpegsnoop by searching the image for the string FF C0 00 11 08 and checking the next 4 bytes. If one says 6F and I know it's 1 pixel short of a full MCU, just change it to 70 and the dimension is now an even, fully transformable row.

"Nonetheless, I can perform the lossless extend very easily myself just by modifying the SOF0 "Samples per Line" from 239 to 240. Using a hex editor I changed the value and the lossless extension worked. This is possible to do since we're not actually increasing the number of MCUs, just simply adjusting the crop that happens during rendering.

For the sake of completeness, one could also update the SubIFD ExifImageWidth as well."

I tried to figure out how exactly to do this and where to make the changes inside each image, but was unable to do so. Also, I don't know how one might automate this sort of editing with thousands of images. In any case, I'm glad to report that thanks to Guido Vollbeding the bug has finally been fixed in JpegTran, so the truncation should no longer happen with the latest release. :)
 Sure... in the case of modifying the SOF0 "Samples per Line", you can find this 16-bit value located at bytes 7 and 8 after the OFFSET reported by JPEGsnoop for SOF0. For example, if JPEGsnoop reports Marker: SOF0 at OFFSET 0x00005BCF, then you can adjust the 2-byte hex value at 0x5BD6-0x5BD7. In this case, I modified the value from 0x00EF to 0x00F0.

Finding the SubIFD ExifImageWidth is quite a bit harder since it requires traversing the IFD directory entries looking for the tag.

Automating this is not really possible (especially if you were to do the ExifImageWidth change as well) unless you were to script it or write a short program. Fixing JpegTran is definitely the way to go :) That is excellent news that Guido corrected this JpegTran bug!
 Hi Calvin,

First of all, thanks for replying!

Next, let me clarify what I'm trying to achieve. Let's say I have a full 256x256 image named (F), and a 239x256 one with partial MCUs at the right edge named (P). Both have 16x16 MCUs. I want to losslessly join them as (F)(P), not as (P)(F), so the partial MCUs should end up at the right edge of the final image as well.

To achieve this, here's what I did:

1) Used JpegTran to losslessly (negative) "crop" (F) to extend it to and saved it as (T1) with size 495x256 (495 = 256 + 239).
2) Used JpegTran to losslessly drop (P) shifted right by 256px onto (T1) and saved it as (T2).
3) Confirmed that (T2) contains (F) in its left portion, and (P) in its right portion, since I want an (F)(P) join.

Expected Result: (T2) should contain all of (F) and all of (P).

Actual Result: (T2) contains all of (F), but at the right edge (P)'s content is truncated, and the partial MCUs are chopped off. Since (P) has partial MCUs of size 15x16, I end up with the right-most 15px of (T2) as being totally blank. In other words, since (P) has 14x16px full MCUs + 15px extra in a partial MCU = 239px total, only 14x16 = 224px survived in the final image (T2), and the remaining 15px was blank.

I hope I'm explaining this properly... :-/

Basically, I ended up with (F)+(P) = (256)+(239) = (16x16)+(14x16+15) = (256px of F)+(Left 224px of P)+(Blank right 15px)

Note that in step 1) above I tried extending (F) to 496x256, as well as 512x256. All that did was add extra blank pixels to the right, but (P)'s right-most portion was still truncated in the final image.

[ The above example can be replicated with (P) being joined below (F), where (P) is now 256x239px. This of course means (T2) is now 256x495, and the bottom 15px of (P) gets lopped off. ]

If I go by your reply above, it should be very much possible to "losslessly join them as [I am] appending (P)'s MCUs immediately after (F)'s full MCUs", right? So what's the problem here? Is it a bug in JpegTran or a limitation of the tool?

Since I simply cannot afford to lose up to 7px at right/bottom (if MCU size is 8), or up to 15px at right/bottom (if MCU size is 16), how do I solve this issue? If I need to use something else for crop+drop, what do you suggest, and is there a way to batch automate/script it so I can repeat this on thousands of images?

I don't mind minimum number of black pixels padding the right or bottom edges of the final image, as long as all the original image data is present and it's all done losslessly.

Thanks for all your help!

P.S. I deliberately chose 239px in the example above, because it is 1px less than a multiple of the MCU size 16, and thus results in the maximum possible 15px loss. If the MCU size was 8, then the right-most 7px would be lost instead (since 237 = 29x8+7).
 Hi James -- I tried a quick test with jpegtran and it does appear to be removing the last column of partial MCUs. There may be a command line option to avoid this, but I'm not sure.



From this point, one should then be able to use the standard IJG tools to concatenate the MCUs from (F) and (P).

Good luck!
 Hi Calvin,

I read with interest your reply above to mzatanoskas, although that was more than 3.5 years ago and I don't know whether you still actively monitor comments on this page or not. Sure hope you do, especially since I found no email address at which I could directly get in touch with you.

Just like mzatanoskas, I too recently came to know about lossless JPEG operations and have been researching them since, which naturally lead me to your great site with such wonderful yet easily digestible explanations for us non-technical folk. :)

I have a similar question to ask as well. I am currently trying to patch together a bunch of large multi-megapixel images that have been sliced up into tiny pieces of 256x256px each. All slices therefore have the exact same characteristics, including 16x16 MCUs. The original images unfortunately are all odd-sized, since the last (right-most) column and (bottom) row are made up of images that are not 256px in size and thus have partial MCUs, I guess.

Having downloaded the latest (based on 2012's v8d of IJG's software) JpegTran and JpegJoin, I immediately gave up on the latter because it is a usability nightmare when it comes to joining more than a few images. How could anyone ever think that opening every image *one by one* and adding to the frame list would be a nice workflow, given a 1000 or even a few 100 images?! Haven't they heard of multi-select or drag 'n' drop in this day and age? Anyway... I moved on to JpegTran and after much hair tearing, finally managed to figure out how exactly crop and drop worked. Nice, now on to the actual work. :)

Having cooked up a quick 'n' dirty batch file (yeah, I like to do things the hard way!) to run JpegTran on all the pieces in turn, I waited and while the final results were very nice indeed, something caught my eye which is what I'm here to ask you about. Thing is, the final column and bottom row are getting truncated and the partial MCUs are seemingly being deleted without mercy! So for example if my right-most column piece is 237px in width instead of the full 256px, the image is being truncated till it is a multiple of 16px (the MCU size). Thus I'm ending up with 224px (closest number below 237 that is a multiple of 16) and end up losing 13px to the right of the joined image. Similarly, I'm losing data at the bottom as well (obviously the amount lost differs according to the final image's width and height). Ideally speaking of course, 240 is a multiple of 16 as well and is much closer to 237 than 224 is. Isn't there any way at all that I can extend the last column pieces by 3px each, so that the final 240px piece is not truncated at all? I can live with a few black pixels on the right and bottom, if it means that the entire image as such has not been subjected to further JPEG generational loss.

I tried contacting Guido Vollbeding (author of the crop+drop code in JpegTran and JpegJoin), but unfortunately his JpegClub email address mentioned in JpegJoin seems to have stopped working. Have been breaking my head trying to figure out what to do next, and that is why I am asking you if there is any way *at all* to losslessly 'complete' the right/bottom partial MCUs to prevent image truncation by these two otherwise fine tools. *Please* help!
 If I understand your question correctly, you are trying to "losslessly" join one or more images together, with some containing partial MCUs? Let's take a simple example of two tiles and focus on the horizontal direction for the moment: (A) is 237px in width and (B) is 256px in width.

If you were to arrange the tiles (B) and then (A), it would be possible to losslessly join them as you are appending (A)'s MCUs immediately after (B)'s full MCUs. Depending on which tool you are using to concatenate the images, you might need to force the X dimension of (A) to be 256px instead of 237px, join and then reduce the total dimension from 512px to 493px.

If, instead, you were attempting to arrange the tiles as (A) and then (B), you'll run into a problem doing this losslessly. The last MCU in (A) is a partial MCU. The coefficients in the last MCU have to define the full 16px, but the original image source only had 13px. Therefore, the MCU defines the 16x16 block but the image dimensions instruct the renderer to only display the first 13px. Now, when we want to append another MCU immediately after the 13th pixel, we are going to change the frequency domain representation of (A)'s MCU block. The last 3 pixels will not match what was originally encoded, so we need to redo the FFT to generate a new MCU. The same needs to be done for all of (B)'s MCUs which have now shifted by 3 pixels to the left. After "recompressing", the result may not exactly match the original source and hence it probably won't be "lossless".

An alternate solution, which is probably not what you are after, is to artifically extend (B)'s partial MCU to make it a full MCU (ie. make the tile 240px instead of 237px), and then join. This will be lossless, but you will end up with a 3px vertical strip that may have random content (or black, it is compressor-dependent).

Hope that helps!
 Thanks a lot for the reply Calvin.

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.
 Well, it looks like you're trying to find a very simple solution to a potentially complex problem :) Unfortunately there are a few pitfalls with the idea, but what you are attempting to do may still be possible, with some significant limitations.

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!
 Hey Calvin very interesting article. I only just found out that jpegs could be manipulated losslessly. There were a couple of points that I didn't quite understand though:

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.
 Hi there --

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!)
2008-03-29Martin Herrmann
 Mapivi ( provides a graphic user interface to different command line tools. On of them is jpegtran. There are e.g. functions to lossess add border around a JPEG, to change the aspect ratio without recompression or to add a lossless watermark. All functions use the drop feature of jpegtran that you mentioned in your article.

2008-03-29Martin Herrmann
 Mapivi ( provides a graphic user interface to different command line tools. On of them is jpegtran. There are e.g. functions to lossless add border around a JPEG, to change the aspect ratio without recompression or to add a lossless watermark. All by using the drop feature of jpegtran.

 Thanks Martin!
 how do I make a 16X16 quantisation matrice
 Have a look at my response to the same question posted on 2007-03-19 to my Quantization Tables page.
 Sir i m doing project in JPEG encoder Implemented in FPGA.I Need help fro u.can u send architecture of subsampling block so i will come to know how actually subsampling is done.thanks
 I have a brief summary of this on the JPEG Chroma Subsampling page.


Leave a comment or suggestion for this page:

(Never Shown - Optional)