Lossless conversion from JPG to JXL


I’ve been a keen photographer for a number of years. Not professionally, of course. I mainly just enjoy taking holiday snaps and the odd luxury vehicle at car shows and the like.

As you might imagine, being a digital hoarder has resulted in me having many thousands of photos stored on my various computer systems. The problem I face is that I can’t bring myself to delete even the most grainy, blurred, out-of-focus, photo-bombed shots. For example, a number of years ago I was at a car show, and an original and very rare Jaguar XK120-C was arriving. I rushed and took a few photos as it pulled up which later turned out to be pretty poor. Really, I should just delete these useless photos because as the day progressed I was able to get dozens more, much higher-quality photos of the old Jag. Oh, the joys of being a datahoarder!

Back in the day, I didn’t have the ability to shoot in one of the “RAW” file formats, so the majority of my collection is saved in the good old JPEG (jpg) file format. Over the past few months, I’ve increasingly been reading and hearing some good things about a new more-efficient format known as JPEG XL (jxl) and thought it was about time to look at it a little more closely. I hope that I can convert my old JPEGs into JPEG XL and regain some precious storage space.

What is JPEG XL (JXL)?

JPEG XL is a new, modern file format for storing images. It has been designed with the hope that it will eventually replace the legacy JPEG format. The original JPEG format has been around for over three decades so doesn’t benefit from advances made in areas such as image compression.

JPEG XL is an open standard, with open-source reference libraries already available. It’s able to store images as either lossy or lossless, with comparable lossy images having a smaller file size when saved as JPEG XL compared to the traditional JPEG format.

Why JPEG XL over AVIF or WebP or HEIC?

JPEG XL as a next-generation image file format has a number of competitors. The main ones are AVIF, WebP, and HEIC, which have each been around for several years.

You might therefore be wondering why I’m only now interested in converting my photos to one of these new formats, in an attempt to reduce my storage needs. Well, simply put, I hate the thought of losing any image quality whatsoever. Before JPEG XL came along, this would have meant that I would have had to convert my collection to the lossless version of either AVIF, WebP, or HEIC. In fact, I have experimented with doing this in the past and found in each case, the conversion to lossless actually increased the filesize. The opposite of what I wanted!

What piqued my interest in JPEG XL is that it’s been designed with the ability to transcode legacy JPEG images to the new XL format without any loss of quality. In fact, its authors even claim that you could transcode or convert the JPEG XL back to legacy JPEG and end up back with your original image.

If the officially published figures are to be believed, this means I’d be able to convert, or transcode, my entire collection of photos and enjoy a space-saving of 20%. Given the number of photos and the current cost of storage, 20% represents a significant saving.

Finding the best software for converting the images from JPEG to JPEG XL

At the time of writing, the JPEG XL standard is still in the process of being implemented by a number of “big player” software developers. For example, I couldn’t use my version of Adobe Photoshop to perform the conversion as it doesn’t yet support the format.

I tried a few other applications that do support the format, such as GIMP and ImageMagick but had limited success. Remember, for me the criteria is to be able to convert my legacy JPEG images to lossless JPEG XL in order to save space, but crucially, to be confident that no image quality has been lost and to even be able to convert them back to their original format, if desired. I couldn’t reliably achieve this with GIMP, ImageMagick, etc.

It would seem that a number of applications that support JPEG XL don’t actually take advantage of the format’s ability to be transcoded from JPEG. Often, the resulting JPEG XL was actually larger than the original JPEG. This suggests the software had simply done a “traditional” conversion, in the same way that it might convert a Bitmap, PNG, or TIFF file to JPEG XL.

I then tested the reference implementation, a command line tool known as libjxl and it appears to work perfectly.

Using libjxl to convert (transcode) JPEG to JPEG XL

I’m on a Windows workstation at the moment, so I simply downloaded the latest Windows binary. If you’re using Linux or MacOS, there are also packages available for those systems or if you’re feeling brave, you could of course compile it from source.

Once libjxl is installed it’s really easy to perform the conversion/transcode using “cjxl”, with minimal parameters. In fact, for my use case, I simply need to specify the input and output files. This is because when the input image is a lossy JPEG, the default is to convert it to a lossless JPEG XL.

You can see in the image below that cjxl has done as we wished, taking in the image named intempo.jpg and outputting the reduced size, yet lossless intempo.jxl:

Using cjxl to convert/transcode a file named intemp.jpg to intempo.jxl
Using cjxl to convert/transcode a file named intemp.jpg to intempo.jxl

Taking a look at the new “intempo.jxl” in GIMP, it looks great, indistinguishable from the original:

Viewing the new JPEX XL (.jxl) file in GIMP

Converting JPEG XL back to legacy JPEG

As mentioned, the reason I’m interested in using JPEG XL as the next-generation format for my images is because of its ability to retain the original image quality. That is, if I wanted to convert my new JXL files back to JPG, I could do so. This would stop me from doubting that the image had lost something, compared to the original.

Luckily the libjxl implementation includes a tool called “djxl”, which is a JPEG XL decoder. We can pass it our JXL file and ask it to output the original JPG.

Like cjxl (the JPEG XL encoder), the syntax is very simple. We just need to pass it the JPEG XL filename as the first parameter and the desired output filename as the second:

Converting the JPEG XL back to legacy JPEG
Converting the JPEG XL back to legacy JPEG

To prove that the original intempo.jpg and the newly created intempo_decoded.jpg are identical, we can check the hashes of both files. I’m going to use the Get-FileHash PowerShell cmdlet as I running Windows, but if you wished to perform the test yourself on Linux or Mac OS, you could use something like md5sum.

The hashes of the two files are shown below:

Using PowerShell to generate MD5 hashes of the original image and the new JXL
Using PowerShell to generate MD5 hashes of the original image and the new JXL

As you can see, they are identical. This means not only are the two images visually identical, but they are in essence the exact same file, even containing the exact same metadata.

Batch conversion of JPG to JXL with libjxl

Now that I’m confident I’m not losing any image quality when converting to JPEG XL, and that there’s a reduction in file size, it’s time to look into converting some images en masse. I’ve read the help file for libjxl and there’s no mention of a batch mode.

This of course isn’t a huge problem. Because the software is a command line tool with no GUI, it’s easy enough to write a script to perform the conversion/transcode.

As I’ve mentioned a couple of times, I’m running Windows at the moment so I can write a script in something like Batch, VBS, PowerShell, etc. If you’re running Linux, I’m sure you won’t struggle to automate the conversion using a Bash script.

As it’s a simple task, I’m going to choose PowerShell because I can get away with a “one-liner” for this task:

gci -Path .\samples\*.jpg -File | % {Start-Process -FilePath ./cjxl.exe -ArgumentList $_,$_".jxl" -Wait}

Basically, this looks in the “samples” directory and passes the filenames of jpg files to cjxl for processing. I’ve chosen to simply append “.jxl” to the converted images. I quite like this idea because it’ll allow me to easily identify which images were originally legacy JPEG, should I ever wish to convert them back in the future. If you want your processed images to just have the “.jxl” extension then you’ll need to tweak the one-liner to strip off the .jpg part of the filename. I’ll leave this as an exercise for the reader!

You’ll notice that I’ve used “-Wait” so that PowerShell doesn’t spawn hundreds of instances of cjxl.exe. If you’ve got an enormous image collection to convert and a relatively powerful multi-core computer, you might to tweak the script to run a few instances of cjxl in parallel.

Summary (tl;dr)

I have thousands of photographs, all in legacy JPEG format, consuming an enormous amount of space on my NAS. Rather than buying more storage, I’ve decided to convert the files to a more efficient file format. As I don’t wish to sacrifice any image quality whatsoever, the new next-generation format needs to store the images as “lossless”.

The only next-generation format that fits the bill is JPEG XL (JXL) thanks to its ability to “transcode” from legacy JPEG to JPEG XL and back again.

At the time of writing, there’s a bit of war going on between these next-generation formats like JPEG XL, WebP, AVIF, and HEIC. In fact, Google have recently announced that their Chrome browser will not support JPEG XL, so websites with JXL images won’t render properly. This is because Google developed WebP, one of JPEG XL’s main competitors.

In some ways, however, all of this doesn’t really matter to me. I simply want a way to store my static data, in an efficient format, and to that end, JPEG XL is perfect. I’ll be honest, there’s also a fondness for the old JPEG format even though it’s 30 years old. Nostalgia I guess.

2 thoughts on “Lossless conversion from JPG to JXL”

  1. Many thanks for this article ! There are plenty of nerdy articles out there, but this is what a photographer really wants to know.

  2. Already switched my phone to HEIC and H.265. So I’m only converting legacy JPGs for fun since storage these days is cheaper than ever 🙂
    Ended up running a CMD code instead:

    @echo off

    set “cjxlPath=C:\jxl-x64-windows-static\cjxl.exe”
    set “sourceDirectory=R:\Camera\JPG”
    set “destinationDirectory=R:\Camera\JXL9”
    set “effortLevel=9”

    if not exist “%destinationDirectory%” mkdir “%destinationDirectory%”

    for %%F in (“%sourceDirectory%\*.jpg”) do (
    “%cjxlPath%” -e %effortLevel% “%%F” “%destinationDirectory%\%%~nF.jxl”

    echo Conversion complete.


Leave a Comment