Add Linear Output export — dump decoded source data (Pakon, DNG, camera RAW) as untouched 16-bit TIFF - #722
Merged
Conversation
New export intent (Print / Flat / Linear) that decodes the source file and writes an untagged linear 16-bit TIFF, skipping the entire darkroom pipeline. Supports Pakon RAW (4× expansion), LinearRaw DNG (3ch SilverFast + 4ch VueScan with IR), and camera RAW (unity WB, sensor- native colour). Camera WB is embedded in XMP as RAW-WB (MakeTiff- compatible format). Source metadata (Make, Model, DateTime) is preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Export All/Selected now passes the file list through to request_linear_output_export instead of always exporting only the current frame. - Remove the save-file dialog; write directly to the export folder with overwrite-protection naming (_2, _3, …), matching flat/print. - Add adjust_maximum_thr=0.0 to _decode_camera_raw so LibRaw uses the camera's fixed white level instead of per-frame rescaling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Expansion combo box: source-type-dependent options (Pakon F135: 4×/2×/off, F335: off/2×/4×, DNG: off/2×/4×, camera RAW: N/A) - F335 (16-bit sensor) defaults to no expansion; detected by file size - 2k Square/Panoram assumed 14-bit (unverified, documented) - ImageDescription records source loader/format, expansion factor, WB status - Camera RAW exports include as-shot WB in ImageDescription - Pakon exports populate TIFF Make/Model from PAKON_SPECS - Camera RAW exports read Make/Model via tifffile for non-DNG formats - Updated USER_GUIDE.md and PIPELINE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- EXIF fallback: scan for embedded EXIF TIFF block in non-TIFF RAW formats (RAF, ORF, etc.) to extract Make/Model/DateTime - Camera RAW exports include as-shot WB gains in ImageDescription (e.g. "no WB applied (as-shot: 1.318 1.000 2.891)") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a new export option, "Linear Output," that writes whatever was decoded from a loaded source file as a plain 16-bit TIFF — before any pipeline processing (Normalization, Exposure/Print, Lab, Toning, Finish), no color management, no white balance baked into the pixel data. Inspired by tools like RawDigger, MakeTiff, and RawTherapee's "Save Reference Image" — all of which export a similar linear/unrendered TIFF for sensor calibration, device profiling, or diagnostic purposes.
Supports all three loader types: Pakon proprietary RAW, DNG LinearRaw (SilverFast/VueScan), and camera RAW.
Why
Characterization/profiling work — building an ICC input profile or otherwise measuring a scanner or camera's actual response requires genuinely unprocessed device data, not a normal export, which has already been through white balance, and NegPy's own tone shaping by the time it leaves the app.
Format conversion and archival. Pakon's proprietary .raw format is a headerless binary blob that almost nothing outside NegPy and a few other apps can open. Converting to a standard 16-bit TIFF means the data is readable by any image tool, archivable long-term, and not dependent on a single application's bespoke loader. The same applies to LinearRaw DNG variants — while DNG is a published format, many tools don't handle the LinearRaw IFD, so a plain TIFF is more universally accessible. (Longer term, I'm hoping to add support for a few other exotic scanner formats too — see "Out of scope" below for what's already on the radar.)
RGB triplet/stitch consolidation (planned follow-up, not in this PR). Narrowband/RGB-scan captures currently exist as separate per-band source files — three for a single triplet, and a multiple of three for any stitched multi-part capture. This scales fast: a 4-part stitch is already 12 raw files behind one finished image. The RGB-scan stitch support that just landed (stitch_triplets, one (green, blue) pair per part) makes it practical to collapse all of those into a single combined linear TIFF per finished composite, regardless of how many raw parts went into it. Not included in this first version; planned as a follow-up now that the underlying stitch/merge mechanism exists.
White balance is recorded, not applied. Camera RAW exports write the camera's as-shot WB gains into XMP metadata (MakeTiff-compatible, see Metadata below) and into the human-readable ImageDescription tag (e.g.
no WB applied (as-shot: 1.318 1.000 2.891)), rather than baking them into the pixels. This is deliberately safer — once WB gains are multiplied in and rounded to 16-bit integers, any channel that clips can't be perfectly recovered. Recording the gains and letting a downstream tool apply them is exact; reversing an already-applied WB is not.No per-frame content-dependent scaling. The expansion factor (a linear-domain exposure shift for sources whose ADC precision doesn't fill the 16-bit container) is never a function of an individual frame's own content. This constraint is directly informed by #718 — the exact same category of bug (LibRaw's
adjust_maximum_thrsilently rescaling by a frame's own brightest pixel, breaking the assumption that a roll/batch shares one consistent scale) would have been trivial to reintroduce here via naive auto-detection, so it was deliberately avoided:PAKON_SPECS. F135 (confirmed 14-bit) defaults to ×4 — the standard 2-bit shift factor (2¹⁶/2¹⁴), which also fixes a usability issue where MakeTiff's ×2 leaves thumbnails too dark to distinguish frames. F335 (claimed 16-bit, unverified — no sample file available) defaults to ×1/off to avoid clipping. 2k Square and Panoram default to ×4 (F135's factor) rather than F335's ×1 — these are believed to be F135-family hardware variants (same 14-bit ADC), and F335 units are rare enough, with uncertain panorama capability in the first place, that F135-based is the far more likely source for any real file in either of these specs. ×1/×2/×4 manual override remains available for the rare case this assumption is wrong.Independent decode path. Camera RAW decode uses its own
rawpy.postprocess()call — it does not reuse_decode_sensor_rgb. The rawpy parameters are equivalent to the shared function'slinear_raw=Truemode; the reason for a standalone path is that linear output needs to capture WB gains and timestamp from the raw object before closing it, which_decode_sensor_rgbdoesn't return — and threading those extras through its return type and all its callers wasn't worth it for a single consumer.adjust_maximum_thr=0.0is set on both paths independently.IR channel handling
DNG LinearRaw sources with a 4th (IR) sample get split into two output files: RGB as the primary TIFF, IR as a separate grayscale TIFF with an
_irsuffix. Not folded as a 4th sample since most TIFF viewers assume a 4th channel is alpha. Pakon and camera RAW never carry IR.Metadata
dc:descriptionin MakeTiff's exact format (RAW-WB: R G B, green-normalized to 1.0), source filename incrs:RawFileName. This field is reserved exclusively for that format — nothing else is appended to it.no WB applied (as-shot: 1.318 1.000 2.891)).PAKON_SPECSentry (e.g.Make="Pakon",Model="F135 Plus High Res"), since Pakon's format carries no embedded device metadata.Out of scope (explicitly deferred)
Risk
New export option only — doesn't touch the existing render/export/ICC pipeline. No risk to existing saved edits or normal exports.
Testing
tests/test_linear_output.py— 52 tests:test_f335_detected_by_size,test_f135_gets_4x,test_source_type_f335/f135)test_4ch_dng_*)test_3ch_dng_*)test_build_xmp_maketiff_format,test_normalize_wb_rgb)docs/USER_GUIDE.mdanddocs/PIPELINE.mdupdated.