Batch-develop a folder of raw photos into a punchy, hand-tune-ready JPEG baseline — so you spend your editing time polishing the keepers, not developing every frame from scratch.
.RAF + refinery: rafinery leans on darktable
for the raw heavy-lifting (X-Trans/Bayer demosaic, highlight recovery, color,
lens correction) and then applies its own scriptable punch grade — local
contrast, contrast, vibrance and capture sharpening, plus optional ISO-gated
denoise. The result is livelier than a neutral default render and a sane
starting point for Lightroom, Capture One, or darktable itself.
Built for Fujifilm X-series shooters (hence the name), but works on any raw
format darktable can read: .raf .cr2 .cr3 .nef .arw .dng .orf .rw2.
Modern raw engines are meaningfully better than the JPEG engine in a camera from a few years ago, and a whole trip's worth of raws is too much to develop by hand. rafinery gives you a consistent, opinionated baseline across the entire set in one command — then you hand-tune only the shots worth the time.
Your originals are never modified: rafinery only reads raws and writes JPEGs to a separate output folder.
- Python ≥ 3.11 with
numpyandPillow - darktable (provides
darktable-cli) - exiftool — optional, for copying EXIF (dates, camera, exposure) into the output and for ISO-gated denoise. Without it, rafinery still runs; it just skips metadata copying and denoise.
pip install rafinery # once published
# or from source:
git clone https://github.com/tulensrma/rafinery && cd rafinery
pip install -e .If darktable-cli isn't on your PATH, point rafinery at it with
--darktable /path/to/darktable-cli or the RAFINERY_DARKTABLE env var.
Pick a look first — build a side-by-side comparison sheet of the punch levels on a few auto-selected frames:
rafinery preview ~/shoot/raws preview.jpgThen develop the whole folder:
rafinery develop ~/shoot/raws ~/shoot/edited --level punchyOptions:
| Flag | Meaning |
|---|---|
--level {natural,punchy,extra} |
punch strength (default punchy) |
--no-denoise |
disable ISO-gated chroma denoise |
--darktable PATH |
explicit darktable-cli location |
develop is resumable — it skips files whose output already exists, so you
can re-run after an interruption. Per-file failures are counted and skipped
rather than aborting the run.
| Level | Character |
|---|---|
natural |
gentle lift; subtle improvement over a neutral render |
punchy |
balanced and lively — the recommended baseline |
extra |
bold; great on landscapes, can oversaturate greens/skin |
Tune them or add your own in src/rafinery/grade.py
(LEVELS).
rafinery analyze measures per-image sharpness (variance-of-Laplacian,
global and tiled center/corner) and haze/contrast (black-point, dynamic
range, dark-channel, saturation) — and can detect whether your lens condition
changed after a suspected event (a smudge, a knock, a cleaning).
# per-image metrics to a CSV
rafinery analyze ~/shoot/jpegs --csv quality.csv
# before/after a suspected lens change, with a corner-crop sheet of suspects
rafinery analyze ~/shoot/jpegs --split "2024-08-01 14:00" --sheet suspects.jpgThe comparison is built to avoid fooling yourself: it only compares matched
apertures, and it establishes a control baseline by splitting the
before-period in half — so you can see whether the before/after change exceeds
the normal shot-to-shot wander. The corner-crop sheet is the best way to spot a
localized smudge by eye. Operates on JPEG/PNG/TIFF; --split needs exiftool.
See docs/how-it-works.md for the pipeline details.
MIT — see LICENSE.