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:
- Open Lightroom (wait 30 seconds for it to load)
- Import photos (wait for previews to generate)
- Apply a preset to all images
- Manually adjust the ones that didn’t work well with the preset
- Export to JPEG (wait again)
- 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
--resize 2000 # Max dimension of 2000px (maintains aspect ratio)
--resize 50% # Resize to 50% of original
Format and Quality
--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
--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:
--web-version
--web-size 1200 # Max dimension for web version
--web-quality 85 # Quality for web version
Output Directory
--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:
./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:
./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:
- White Balance — Temperature and tint corrections first, as they affect everything else
- Auto-Level — Histogram stretching to use full dynamic range
- Auto-Gamma — Midtone brightness optimization
- Highlight Recovery — Compress bright values if clipping was detected
- Shadow Recovery — Lift dark values if needed
- Contrast — Sigmoidal contrast for natural-looking results
- Clarity — Local contrast enhancement via large-radius unsharp mask
- Modulate — Brightness and saturation adjustments
- Vibrance — Smart saturation boost
- Noise Reduction — If enabled
- Sharpening — Final unsharp mask
- Resize — If requested
- Watermark — If requested
- Save — With quality settings
- Web Version — If requested
Real-World Usage Examples
Basic processing with intelligent analysis:
cd /path/to/photos
./process_raw_photos.sh
Event photography workflow:
./process_raw_photos.sh \
--preset portrait \
--noise-reduction 20 \
--resize 4000 \
--output-dir "./Jazz Night - Dec 2025" \
--web-version \
--watermark "© Marcos Fermin"
Product photography:
./process_raw_photos.sh \
--preset vivid \
--contrast 15 \
--clarity 25 \
--saturation 115 \
--format png \
--resize 2000
High-ISO concert photos (noisy, low light):
./process_raw_photos.sh \
--shadows 30 \
--noise-reduction 50 \
--sharpen 0.4 \
--vibrance 20
Fine art black and white:
./process_raw_photos.sh \
--preset bw \
--contrast 20 \
--clarity 30 \
--format tiff
Quick web export:
./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:
brew install imagemagick
Ubuntu/Debian:
sudo apt-get install imagemagick bc
Setup:
# 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.