RAW Photo Batch Processor: A Command-Line Tool for Photographers

As a photographer who shoots in RAW, I’ve always found the workflow of processing large batches of photos tedious. Open Lightroom, wait for it to load, import, apply presets, export — it works, but sometimes you just want something faster and more scriptable. After a recent gig where I had over 300 photos to process, I decided to build my own solution.

The result is the RAW Photo Batch Processor — a professional-grade bash script that batch processes Canon RAW (.CR2) files with intelligent per-image analysis, professional presets, and comprehensive editing tools.

 

The Problem I Was Trying to Solve

After a jazz gig or photo session, I often end up with hundreds of .CR2 files sitting on my drive. The traditional workflow looks something like this:

  1. Open Lightroom (wait 30 seconds for it to load)
  2. Import photos (wait for previews to generate)
  3. Apply a preset to all images
  4. Manually adjust the ones that didn’t work well with the preset
  5. Export to JPEG (wait again)
  6. Create web-sized versions for social media (more waiting)

For a batch of 200 photos, this easily eats up an hour or more of my time — and most of that is just waiting.

What I wanted was something that could:

  • Analyze each image individually and calculate optimal corrections based on that specific photo’s characteristics
  • Apply professional-looking edits without manual intervention
  • Run from the command line so I could automate it or run it overnight
  • Stay lightweight — no heavy desktop apps, no subscriptions, no cloud uploads
  • Be customizable — let me tweak settings when I need to, but smart enough to work well with defaults

 

The Solution

The RAW Photo Batch Processor uses ImageMagick under the hood to deliver high-quality results. But the real magic is in the intelligent per-image analysis: before processing each photo, the script examines its exposure, contrast, dynamic range, and color cast, then calculates corrections tailored to that specific image.

This means a well-exposed photo gets minimal adjustments, while an underexposed shot from a dimly-lit venue gets the shadow lift and brightness boost it needs — all automatically.

 

Key Features

Intelligent Per-Image Analysis

This is the heart of the script. For each image, the analyzer examines:

Metric What It Measures How It’s Used
Mean Brightness Average luminosity (0-255) Determines if image is under/overexposed
Standard Deviation Contrast indicator Identifies flat or overly contrasty images
Min/Max Values Dynamic range Detects clipping issues
Channel Means (R/G/B) Per-channel averages Identifies color casts
Clipped Highlights % of pure white pixels Triggers highlight recovery
Clipped Shadows % of pure black pixels Triggers shadow lifting

 

 

Based on this analysis, the script calculates corrections:

  • Dark image (mean < 80)? Increase brightness proportionally
  • Bright image (mean > 180)? Decrease brightness to prevent blowouts
  • Flat histogram (StdDev < 40)? Boost contrast for more punch
  • Too contrasty (StdDev > 70)? Reduce contrast slightly
  • Highlights clipping > 2%? Apply highlight recovery
  • Shadows clipping > 2%? Lift shadows to reveal detail
  • Warm color cast detected? Apply cooling temperature adjustment
  • Cool color cast detected? Warm it up

Seven Professional Presets

Rather than building everything from scratch each time, I included presets optimized for different shooting scenarios:

auto (Default) The intelligent analysis preset. Analyzes each image and calculates optimal corrections automatically. Best for general use and mixed lighting situations where each photo needs different treatment.

portrait Designed for people photography. Reduces contrast slightly for a softer look, lifts shadows to flatter skin, reduces clarity to smooth texture (without looking fake), and uses subtle sharpening. The vibrance boost adds color pop while protecting skin tones from over-saturation.

Contrast: -10 | Highlights: -20 | Shadows: +15
Saturation: 95% | Vibrance: +20 | Clarity: -15
Sharpening: 0.3

vivid Punchy and bold. Perfect for landscapes, product photography, and food shots where you want colors to pop. High contrast, boosted saturation, strong clarity for that “wow” factor.

Contrast: +20 | Highlights: -10 | Shadows: +10
Saturation: 125% | Vibrance: +40 | Clarity: +25
Sharpening: 0.7

soft Dreamy and muted. Great for artistic shots, romantic portraits, or lifestyle photography. Low contrast, lifted shadows, reduced saturation for that ethereal look.

Contrast: -15 | Highlights: +10 | Shadows: +20
Saturation: 85% | Clarity: -25 | Sharpening: 0.2

bw Professional black and white conversion. Full desaturation with boosted contrast and clarity for that classic, dramatic monochrome look. The intelligent analysis still runs for exposure optimization.

Saturation: 0% | Contrast: +15 | Clarity: +20
Sharpening: 0.6

vintage Warm and faded. That nostalgic, film-like aesthetic with warm tones, slightly faded highlights, and reduced saturation.

Temperature: +25 | Contrast: -5 | Highlights: +15
Shadows: +10 | Saturation: 90% | Vibrance: -10

natural Minimal processing. For when you want true-to-life colors with just basic optimization. Straight conversion with subtle sharpening, no creative adjustments.

All adjustments: 0 | Sharpening: 0.3

Full Manual Control

When presets aren’t enough, you have granular control over every aspect of the edit:

Tone Adjustments

  • --contrast (-100 to +100): Overall contrast. Negative values flatten the image, positive values add punch.
  • --highlights (-100 to +100): Control the brightest areas. Negative values recover blown highlights.
  • --shadows (-100 to +100): Control the darkest areas. Positive values lift shadows to reveal detail.
  • --clarity (-100 to +100): Local contrast / midtone punch. Negative values soften, positive values add definition.

White Balance

  • --temperature (-100 to +100): Color temperature. Negative = cool/blue, positive = warm/yellow.
  • --tint (-100 to +100): Green/magenta balance. Useful for correcting fluorescent lighting.

Color

  • --saturation (0 to 200): Overall color intensity. 100 = no change, 0 = grayscale.
  • --vibrance (0 to 100): Smart saturation that boosts muted colors more than already-saturated ones. Protects skin tones.

Correction

  • --noise-reduction (0 to 100): Reduces digital grain. Higher values = stronger reduction (but may lose detail).
  • --sharpen (0 to 2): Sharpening amount. Default 0.5 works for most images.

Output Options

Resize

bash
--resize 2000      # Max dimension of 2000px (maintains aspect ratio)
--resize 50%       # Resize to 50% of original

Format and Quality

bash
--format jpg       # JPEG (default) - best for web and sharing
--format png       # PNG - for graphics or when you need transparency
--format tiff      # TIFF - for print or further editing
--quality 90       # JPEG quality (1-100, default is 100)

Watermarks

bash
--watermark "Photo by Marcos Fermin"
--watermark-position bottomright    # topleft, topright, bottomleft, bottomright, center
--watermark-opacity 30              # 0-100

Web Versions Automatically generate smaller, web-optimized copies alongside your full-size exports:

bash
--web-version
--web-size 1200      # Max dimension for web version
--web-quality 85     # Quality for web version

Output Directory

bash
--output-dir ./processed
--output-dir "/Users/marcos/Photos/Edited"

Analysis and Preview Modes

Analysis Mode Before committing to a full batch process, you can analyze all images to see what corrections would be applied:

bash
./process_raw_photos.sh --analyze
```

This outputs detailed metrics for each image:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Image: IMG_0001.CR2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Exposure Analysis:
  Mean Brightness:    95.3 / 255
  Dynamic Range:      12.0 - 248.5
  Contrast (StdDev):  52.4
  Status:             underexposed

Highlight/Shadow Clipping:
  Clipped Highlights: 1.2%
  Clipped Shadows:    3.5%

Color Analysis:
  Red Channel:        98.2
  Green Channel:      94.1
  Blue Channel:       93.6
  Color Cast:         warm

Recommended Corrections:
  Brightness:         115%
  Contrast:           5
  Highlights:         0
  Shadows:            7
  Temperature:        -15

Preview Mode Test your settings on a single image before processing the entire batch:

bash
./process_raw_photos.sh --preview IMG_0001.CR2 --preset portrait --vibrance 30

This processes just that one file so you can check the results before committing to a full batch run.

The Processing Pipeline

When you run the script, enhancements are applied in a specific order optimized for quality:

  1. White Balance — Temperature and tint corrections first, as they affect everything else
  2. Auto-Level — Histogram stretching to use full dynamic range
  3. Auto-Gamma — Midtone brightness optimization
  4. Highlight Recovery — Compress bright values if clipping was detected
  5. Shadow Recovery — Lift dark values if needed
  6. Contrast — Sigmoidal contrast for natural-looking results
  7. Clarity — Local contrast enhancement via large-radius unsharp mask
  8. Modulate — Brightness and saturation adjustments
  9. Vibrance — Smart saturation boost
  10. Noise Reduction — If enabled
  11. Sharpening — Final unsharp mask
  12. Resize — If requested
  13. Watermark — If requested
  14. Save — With quality settings
  15. Web Version — If requested

Real-World Usage Examples

Basic processing with intelligent analysis:

bash
cd /path/to/photos
./process_raw_photos.sh

Event photography workflow:

bash
./process_raw_photos.sh \
  --preset portrait \
  --noise-reduction 20 \
  --resize 4000 \
  --output-dir "./Jazz Night - Dec 2025" \
  --web-version \
  --watermark "© Marcos Fermin"

Product photography:

bash
./process_raw_photos.sh \
  --preset vivid \
  --contrast 15 \
  --clarity 25 \
  --saturation 115 \
  --format png \
  --resize 2000

High-ISO concert photos (noisy, low light):

bash
./process_raw_photos.sh \
  --shadows 30 \
  --noise-reduction 50 \
  --sharpen 0.4 \
  --vibrance 20

Fine art black and white:

bash
./process_raw_photos.sh \
  --preset bw \
  --contrast 20 \
  --clarity 30 \
  --format tiff

Quick web export:

bash
./process_raw_photos.sh \
  --preset natural \
  --resize 1920 \
  --quality 85 \
  --output-dir ./web

Performance

Processing speed depends on your hardware:

System Speed 100 Images
Apple Silicon (M1/M2/M3) ~3 sec/image ~5 minutes
Intel Core i7 ~5 sec/image ~8 minutes
Intel Core i5 ~7 sec/image ~12 minutes

 

 

Additional operations add time:

  • Intelligent analysis: +0.5 sec/image
  • Web version generation: +1 sec/image
  • Heavy noise reduction: +1-3 sec/image

For a typical batch of 200 photos from a gig, I’m looking at about 10-15 minutes on my M1 Mac — and I can start it and walk away. Compare that to the hour+ of active clicking in Lightroom.

Installation

Requirements:

  • ImageMagick
  • bc (calculator, usually pre-installed on macOS/Linux)

macOS:

bash
brew install imagemagick

Ubuntu/Debian:

bash
sudo apt-get install imagemagick bc

Setup:

bash
# Download the script
git clone https://github.com/marcosfermin/process_raw_photos.git

# Make it executable
chmod +x process_raw_photos.sh

# Optionally add to PATH for global access
echo 'export PATH="$PATH:/path/to/process_raw_photos"' >> ~/.zshrc

Supported RAW Formats

The script is configured for Canon .CR2 by default, but you can modify the INPUT_EXTENSION variable in the script for other formats:

 

Brand Extensions
Canon CR2, CR3
Nikon NEF, NRW
Sony ARW, SRF
Fujifilm RAF
Olympus ORF
Panasonic RW2
Pentax PEF, DNG
Adobe/Leica DNG

 

Get It

The project is open source under GPL-3.0.

GitHub: github.com/marcosfermin/process_raw_photos

If you shoot RAW and want a lightweight, scriptable, no-nonsense processing tool, give it a try. I’ve been using it for my own photography workflow and it’s saved me hours. Feedback, issues, and contributions are welcome.

leave a comment