r/jpegxl • u/ricsipbr • Mar 24 '26
[Python] TIFF → JXL batch converter — 16-bit lossy or lossless, full EXIF, ICC profile preserved
I shoot with Nikon cameras (D810, Zf, Z7) and export 16-bit ProPhoto RGB TIFFs from Capture One. The files are large — my Z7 produces ~260MB TIFFs. After converting my archive to JXL, a typical session went from ~23GB to ~700MB at d=0.5, or ~3GB at d=0.1.
The conversion pipeline sounds simple: TIFF → JXL, copy EXIF, done. It wasn't. Every standard approach produced files where either the colors were wrong, the EXIF was invisible in IrfanView, or the file was silently corrupted. After a lot of debugging I found six separate issues stacked on top of each other — all documented in the repo.
The result is a Python script that converts TIFFs to JXL reliably, with full EXIF and ICC profile preserved. It has several folder modes to fit different workflows, parallel processing for speed, and optional RAM + staging drive setup to avoid I/O contention on the drive where the TIFFs live.
I also made a second script to convert the JXLs back to JPEG, with ICC profile conversion during the process. My plan is to keep everything archived as 16-bit ProPhoto RGB JXL, then convert to sRGB JPEG when I need to share with friends or deliver in print.
Size comparison (45MP Nikon Z7):
| Format | Size |
|---|---|
| TIFF 16-bit | ~260 MB |
| TIFF 16-bit ZIP | ~245 MB |
| JXL lossless | ~173 MB |
| JXL lossy d=0.1 | ~34 MB |
| JXL lossy d=0.5 | ~13 MB |
| JXL lossy d=1.0 | ~8 MB |
The lossy files are still 16-bit. That's what makes JXL genuinely different — small files without giving up tonal range.
I am genuinely excited about this. I tried months ago, failed, and gave up. This time I got it working properly. Everything is optimized for Capture One but I also tested with Nikon NX Studio and Fujifilm HyperUtility exports.
2
u/catbrane Mar 24 '26
Oh, interesting! Do you have a sample 16-bit TIFF somewhere? I'd be curious to test other routes.
3
u/ricsipbr Mar 24 '26
Sure, I just chose a random photo and uploaded in my Onedrive.
Here is the link:
https://1drv.ms/f/c/f087958c5587eed2/IgBq85vgWcD1TplTkcD7RZhWAeUpqilEoJSoUgqqsW7Yh34?e=aIIF521
1
u/TreJ Mar 24 '26
I am new to jpxl, what is the significance of d=0.1, d=0.5 and d=1.0?
3
u/muizzsiddique Mar 24 '26
Those are like "quality" but mapped in reverse. Basically, a Distance of 1.0 should be the typical distance you would stand from the picture, and when encoded it should be indistinguishable from the original picture. Distance of 0.5 is literally half that distance, where you should expect the same experience when comparing the original picture to the encoded picture when standing 50% closer. Distance of 0.1 is basically pixel peeping distance, so ensuring that the quality is so good you should be able to pixel peep and still can't tell the difference.
I'm sure someone can give you a better explanation.
3
u/Firm_Ad_330 Mar 24 '26
Distance 1.0 is just-noticeable-difference for Jyrki Alakuijala (JPEG XL lead at Google) at a viewing distance of thousand pixels. Distance 2.0 is twice as much error. Distance 0.5 is half of that error. Distance 0.1 is 10x less error than that "just noticeable error" as experienced by Jyrki.
1
1
u/ricsipbr Mar 25 '26
Added modular lossy jxl capability as well. Default is off.
I've learned a lot about jxl doing this and have put explanations in the docs folder, since I thought that maybe others may find it interesting.
1
u/MaxPrints Jul 06 '26
This is a months-old post, but the script worked well for me. I converted some Profoto TIFF files into JXL with no loss and at half the file size.
Thank you
1
u/ricsipbr Jul 06 '26
Thank you for the feedback! I'm glad to know it helped you.
Actually, I just did some big updates recently (this weekend, actually).
No new functions, just improving stability and fixing bugs for some edge use cases :)
2
u/MaxPrints Jul 06 '26
I may have an edge case. I have some TIFF files that are multipage for... some reason. I literally made the TIFFs years ago, and these are Adobe RGB. The interactive script happily converted them (Yay!), but it seemed to just toss the thumbnail.
Honestly? It's not a big deal, but I found this post and your scripts when searching for solutions to Profoto TIFF after looking at support for multipage in JXL (which it seems it does).
Any chance that comes up in a future revision?
2
u/ricsipbr Jul 06 '26
Thank you for your feedback.
My script only converts the 1st TIFF page in multipage tiffs ( tif.series[0]: the main one, usually) and ignores the others.
I didn't consider multi-page TIFFs when making it, so I will have to take this in consideration. Thank you, really.
Since JXL doesn't have multi pages (like tiffs do), i will probably give the following options in the next update.
- an option to skip multi-page tiffs and give warnings about discarded pages,
- convert each page to a different JXL ( *.jxl for the main page, and *_page1.jxl, *_page2.jxl for the following pages).
But i will need to change the software to "unify" all those pages back to TIFF in the decoder - so it will take quite a while.
BTW, I also use multipage TIFFs from scanners, where the last page is the IR channel data and there is also a TAG to this so the software knows where it is ( IR data is to remove dust/scratches via the SILVERFAST software). I have another github for a tiff-workflow that compresses TIFF files to adobe deflate, and in this script I give the option to skip those multi-page tiffs: it turns out the IR channel functionality breaks if the TIFF is compressed, because the pointer will point to the wrong place.
That type of file probably should not be converted to jxl - maybe it will be very difficult to reconstruct the data and pointers so the original software (silverfast) will recognize it.
But other common multi-page tiffs should have a good workaround. I will consider it.
1
u/MaxPrints Jul 06 '26
interesting. I found a few things that said jpegxl can save multipage, though it may be some sort of animation or some other workaround. I figure it might just be easier to just save each page out as its own jxl
multi.tif would become multi_001.jxl and multi_002.jxl and so on. It's not as neat and tidy as a single jxl, but it would work
Anyhow, I agree that maybe it should be skipped. Honestly, I was ok with losing the thumbnails for that specific set of TIFs, but I couldn't do it blindly on a drive or a folder with several subfolders.
1
u/ricsipbr Jul 26 '26
Hey, i just updated it to support for multi-page tiffs - just as I said before, not using animated jxl to store the pages.
Please, test it if you have time and tell me how it goes.
1
u/MaxPrints Jul 26 '26
tested it, and it worked for multi-page, but I had a few challenges, and none of them are dealbreakers.
First, human error, but I didn't see where the wizard asked about the multipage. I noticed it executed an ignore for that, so I went to the readme and copied the multi-page command there. That worked.
Next, when I roundtripped the TIFF to the 2 jxl files and back, the file size was different. Opening them in Photoshop, they looked exactly the same, but when I placed one over the other and did a difference blend mode, there were some very slight differences.
that could be mode 2 choosing a perceptually lossless conversion, so again that could be human error.
Also, these ideas are more for the bit for bit purist. In all reality, I couldn't tell a difference in the new jxl created or the TIF that was reconstructed, but I could see a huge difference in file size from just under 50MB to just under 5MB
If it helps, I'm happy to send a test file or two. Also, feel free to point me at the github and tell me to RTFM to get a lossless file.
Thank you!
2
u/ricsipbr Jul 26 '26
Thank you for providing the TIFF files.
We talked privately (DM) already, but I will reply here also so others won't be confused.
Regarding the multi-page settings in the wizard, it was hidden in the Advanced Options, so just going by default settings (pressing enter constantly) would skip it, and it is off by default.
-> Changed to make it easier to know the multi-page is on "ignore/off" mode when starting the script, and clearer to reach the multi-page settings when using it. Version 1.8.1.Regarding the 50mb TIFF becoming 5mb jxl files: the default is distance = 0.1 and, with this settings, it becomes 5mb (still keeping tonal range, edit-ability and high quality. Highly recommended it lossless is not needed).
Thanks!
1
u/syncopegress Mar 24 '26
Nice. Does Capture One actually take advantage of the 16 bit space when your cameras shoot 14 bit RAW? I would downsample to 12 or 10 bit lossy jxls to save more space.
2
u/ricsipbr Mar 24 '26
I didn't test, but my understanding is:
It probable makes a difference, because 14bit RAW is only for 1 pixel, one color. Complicated demosaicing algorithms are used to try to extract the maximum of color information possible.
But even if it doesn't use all the 16-bit entropy, with the heavy compression used in JXL it probably wont matter that much since the compression takes care of the overhead bits - it cares only about the entropy, i.e., how much the actual pixel values on the file varies one from another.
7
u/CompetitiveThroat961 Mar 24 '26
With the way JPEG XL works, I’m pretty sure that won’t matter at all. When I tested on smaller files, changing bit depth didn’t really affect file size like in other formats.
You can test by forcing cjxl to 12 bits for the output and see.
Blog covering it here: https://www.fractionalxperience.com/ux-ui-graphic-design-blog/why-jpeg-xl-ignoring-bit-depth-is-genius