Repository navigation
v1.17.0 "Fusion"
Fusion
Fusion is what happens after the shutter: several frames of the same scene merged into one picture that none of them could hold alone. A bracket fuses into an HDR frame, a sweep fuses into a panorama, and in Lightroom both come out the other end as a single merged DNG. This release is about both halves of that. Facet now recognises the frames that were shot to be fused, and it can score the files that come out of the fusion.
The first half was a matter of shooting order. Facet told a bracket from a burst by its exposure ladder, and it expected the exposure to step one way in the order the frames were shot: −1, 0, +1. But Canon, Nikon and Sony all offer a base-first order (0, −, +), and many bodies shoot that way. Such a set failed the test and stayed a plain burst, so Culling offered one recommended frame and let the rest go, which is the opposite of what a set shot for merging needs. The ladder is now judged on the frames sorted by exposure. On a 124,886-photo library that took brackets from 226 sets to 588, and not one set found before was lost. The new sets were almost all Canon, which says the old test had been quietly missing most of that library's brackets.
The second half was a file format. Since Lightroom Classic 13, a Merge to HDR or Panorama DNG is DNG 1.7, with every image inside it compressed as JPEG XL, previews included. LibRaw rejects the file. The exiftool fallback added for merged DNGs then finds only a 256 px JPEG thumbnail, which is too small to score from, so the file was still skipped. Facet now decodes the file's own 1024 px preview directly, through tifffile and imagecodecs. This was checked on a real Lightroom Classic 13.1 DNG from a Sony α7 IV.
Added
- Bigger faces in the culling darkroom. The face panel has a Size slider (64–256 px, default 112 px, previously fixed at 64 px), remembered per browser.
- A hover loupe in the darkroom. A magnifier follows the cursor over the main frame, each compare pane and the face crops, showing the full-resolution pixels under the cursor. It is on by default and has a toolbar toggle.
Changed
tifffileandimagecodecsare now required dependencies, for the JPEG XL previews above. Both Docker locks pinimagecodecs2026.3.6, the last release with a wheel for the legacy CUDA image's Python 3.11. That wheel is abi3, so the two Python 3.12 images install it too. All three image bases were checked by installing the pin and decoding the Lightroom file inside the image.
Fixed
- Apple Silicon scans no longer grow to ~50 GB resident on a 36 GB Mac. The MPS memory pool is now emptied after every model rather than once per pass group. Saliency batches and pyiqa forward passes are also capped on MPS, and the VLM tagger's out-of-memory fallback now recognises the MPS error.
- Capping the MPS pool with
PYTORCH_MPS_HIGH_WATERMARK_RATIOalone no longer crashes at the first allocation. Facet now derives a matching low ratio. - The Qwen3.5 tagger no longer crashes with a segmentation fault while loading on a Mac. On macOS, Facet now sets
HF_DEACTIVATE_ASYNC_LOAD=1, which avoids a thread race in the transformers weight loader (huggingface/transformers#48029). - RAW files LibRaw rejects are scored from their embedded preview instead of being skipped. For Lightroom 12 and earlier merged DNGs, and cameras newer than the installed LibRaw, the preview comes through exiftool. For Lightroom 13+ merged DNGs it is decoded as JPEG XL, as described above.
- The panorama pass that runs after every scan no longer drops a settled HDR panorama. The bracket pass had just relabelled that panorama's per-position brackets, and the panorama pass read those overwritten labels back.
- The darkroom's arrow keys navigate again after you use a slider, instead of moving the slider.
- Week capsule titles no longer show a literal
{year}.
Thanks
- @maisun reported, patiently, in discussion #159, where the whole MPS thread of this release started: memory measurements, crash logs, and the Lightroom merged DNGs Facet could not read. In discussion #162 they then asked why their HDR sequences came out as bursts, which is what uncovered the bracket-order bug.
- @ch1y0q fixed the week capsule title in #161.
Upgrade notes
- Source installs need the two new dependencies: run
pip install -r requirements.txt(orpip install -e .) after pulling. Without them a Lightroom 13+ merged DNG is still skipped, and nothing else changes. - Existing libraries pick up base-first brackets on the next scan, or at once with
python facet.py --detect-sequences. It relabels the whole library from stored EXIF with no image decode; on 125k photos it took about ten minutes, almost all of it the panorama pass. Frames that become part of a bracket render uncorrected only afterpython facet.py --refresh-thumbnails. - Lightroom merged DNGs that were skipped before are picked up by rescanning their folder.
Still unverified
- The Lightroom 13+ decode was proven on one real file, a Lightroom Classic 13.1 merge from a Sony α7 IV, plus synthetic DNGs. Its previews are sRGB. Files from later Lightroom versions or other cameras have not been seen.
- The bracket change was measured on one library. Two base-first brackets shot back-to-back could still merge into one six-frame ladder if the second set's base sits half a step off the first. This has not been seen in practice.
- The MPS changes were validated through the reporter's own runs on an M4 Max, not on hardware here.
- The
acceleratepin at 1.15.0 still silences CVE-2026-69112 on the version number alone; upstream has shipped no fix. Unchanged from 1.16.0.
Full changelog: https://github.com/ncoevoet/facet/blob/master/CHANGELOG.md