A suite of Python scripts designed to automate the process of combining individual Red, Green, and Blue RAW film scans into high-quality, 16-bit linear composite TIFF images, and accurately inverting them into positive images. This toolset is perfect for archival film scanning setups that use distinct red, green, and blue monochromatic light sources to capture color film.
- Visual Control Panel: Set directories, select scanning modes, and fine-tune gamma, clipping, margin parameters, and tone curves interactively.
- Real-Time Feed: Stream process log updates dynamically to an event console and browse output positive files in a session gallery.
- On-the-Fly Previews: Dynamic resizing and rendering of 16-bit TIFF composites directly to standard JPEG in the browser without writing duplicate files to disk.
- Manual Batch Jobs: Execute bulk compositing or inversions on-demand on existing film folders.
- Interactive Setup: Prompts for film stock, format, and roll number to automatically generate organized directory structures.
- End-to-End Automation: Runs a hot folder monitor that instantly composites RAW triplets as they are captured and pipes them directly into the inverter.
- Clean File Management: Automatically moves processed and errored RAW files into designated subfolders to keep your working directory tidy.
- Auto-Color Detection: Automatically determines which shot is the Red, Green, or Blue channel by analyzing the average brightness of the RAW data.
- Linear 16-bit Processing: Uses raw sensor data (
rawpy.ColorSpace.raw) bypassing standard sRGB matrices to ensure pure channel data without cross-channel contamination. - Film Base Neutralization: Optional automated color balancing that calculates the 99.9th percentile of brightness to neutralize the unexposed film base color cast.
- Lossless Compression: Optional
zlib/deflatecompression for the output TIFFs to save disk space. - Broad Camera Support: Currently supports
.CR3(Canon) and.RAF(Fujifilm) RAW formats.
- Accurate Density Inversion: Uses true mathematical division for linear data, maintaining contrast across highlights and shadows.
- Auto-Levels & Tone Curve Control: Removes remaining color casts (per-channel clipping), applies a viewing gamma (default 2.2), and supports optional photographic S-curves for punchy contrast.
- Auto-Cropping: Option to physically crop or just ignore film holder borders during auto-level calculation.
- Python 3.7+
- Required Python packages:
rawpy,numpy,tifffile - Optional Web UI packages:
flask,pillow
-
Clone this repository or download the scripts.
-
(Recommended) Create and activate a Python virtual environment to keep dependencies isolated:
Windows:
python -m venv .venv .venv\Scripts\activate
macOS/Linux:
python3 -m venv .venv source .venv/bin/activate -
Install the required dependencies using pip:
pip install rawpy numpy tifffile(Optional) If you plan to use the Web UI, install its dependencies:
pip install -r requirements-web.txtStart the local web server to run the entire suite through your browser:
python web_ui.pyOpen http://127.0.0.1:5000 in your web browser. You can configure scan settings, start/stop hot folder monitoring, view real-time log outputs, run manual batch tasks, and view completed scans in the gallery.
The session manager provides an interactive, fully automated pipeline.
python scanning_session.pyYou will be prompted to enter a root directory, film stock, format, and roll number. The script will create an organized session folder and start monitoring the negatives subfolder.
As you shoot your RGB triplets into the negatives folder, the script will automatically:
- Detect the 3 RAW files.
- Composite them into a 16-bit linear TIFF.
- Invert, auto-color balance, and apply an S-curve to create a positive image.
- Save the final positive to the
positivesfolder. - Move the original RAWs to the
processed_rawsfolder.
The compositor script runs via the command line and requires the path to a directory containing your RAW files.
python compositor.py -i /path/to/your/raw/filesThis will process the files, auto-detect the channels, and output uncompressed 16-bit TIFFs into a new Composites subfolder.
python compositor.py -i /path/to/your/raw/files --neutralize --compress| Argument | Short | Description |
|---|---|---|
--input |
-i |
(Required) Path to the directory containing RAW files (.CR3 or .RAF). |
--compress |
-c |
Enable lossless compression (zlib/deflate) for output TIFFs. |
--neutralize |
-n |
Automatically balance the color channels to neutralize the film base. |
--align |
-a |
Auto-correct exposure alignment between channels (R, G, B) using FFT phase correlation. |
- File Organization: The script sorts all
.CR3and.RAFfiles in the provided directory alphabetically. - Groups of Three: It processes files in sequential groups of 3. You must ensure that every 3 consecutive files represent the Red, Green, and Blue exposures for a single frame.
- Color Independence: Because the script auto-detects the color of the light source used for each shot based on channel luminosity, the 3 shots for a frame can be in any order (e.g., R-G-B, B-G-R, G-B-R), as long as they are grouped together.
- Output: Processed composites are saved as
Frame_01_Composite.tiff,Frame_02_Composite.tiff, etc., in aCompositesfolder inside your input directory.
- The total number of RAW files in the directory must be divisible by 3. If you have misfires or test shots in the folder, remove them before running the script so the sequence isn't thrown off.
- The script expects distinct RGB monochromatic light sources. If a shot is heavily mixed or exposed incorrectly, auto-detection may fail.
The inverter script takes your 16-bit composite TIFFs (or single RAW DNGs) and accurately inverts them, normalizes levels, and applies gamma and contrast curves.
python inverter.py -i /path/to/your/CompositesThis will process the files, invert them, apply default auto-levels (0.1% clipping) and gamma (2.2), and save them into a new Positives subfolder.
python inverter.py -i /path/to/your/Composites --compress --scurve 0.3 --autocrop| Argument | Short | Description |
|---|---|---|
--input |
-i |
(Required) Path to a single 16-bit composite TIFF/RAW DNG file, or a directory containing them. |
--compress |
-c |
Enable lossless compression (zlib/deflate) for output TIFFs. |
--clip |
-p |
Percentile to clip for black/white points (default: 0.1% to ignore dust/scratches). |
--gamma |
-g |
Gamma correction curve to apply (default: 2.2). Set to 1.0 for strictly linear output. |
--scurve |
-s |
Strength of the contrast S-Curve to apply (default: 0.0 = none). Try 0.2 to 0.5 for a film-like punch. |
--margin |
-m |
Fraction of outer edge to ignore when calculating levels (default: 0.03 = 3%). Prevents film holders from skewing brightness. |
--autocrop |
-a |
Physically crop off the outer margins defined by --margin from the final saved image. |
--global-levels |
Stretch levels globally instead of per-channel. Use this if you relied on the compositor's neutralization and want to perfectly maintain that color balance. |