ColorCast is a Python toolkit for color and style transfer between images. It provides histogram matching, mean/std transfer, LUT-based curves (linear, S-curve, contrast), and selective regional color transfer across shadows, midtones, and highlights. Color-vision-deficiency simulation covers deuteranopia, protanopia, and tritanopia; the Daltonization pipeline re-encodes the chromatic information these deficiencies would otherwise hide, shifting it into a channel the affected observer can still perceive.
- 9 Transfer Methods - Choose from multiple algorithms for different effects
- Intensity Control - Smooth slider (0-100%) to blend between original and styled images
- Selective Regional Transfer - Target shadows, midtones, or highlights specifically
- Modular Package - Use as Python API or run GUI/CLI
- Performance Optimized - LRU caching and batch processing support
- Tested - 43% test coverage with 162 passing tests
- Documented - API documentation and examples
- Plugin Architecture - Easy to add custom transfer methods
- Histogram Matching - Classic histogram equalization, preserves local contrast
- Mean/Std Transfer - Statistical color matching using mean and standard deviation
- Lab Color Transfer (Reinhard) - Industry-standard perceptually uniform color transfer in Lab* space
- LUT + Linear Curve - Histogram matching with linear tone mapping
- LUT + S-Curve - Adds smooth contrast enhancement to histogram matching
- LUT + Contrast - Increases overall contrast with power curve
- Selective: Shadows - Transfers colors only in dark regions (luminance < 0.3)
- Selective: Midtones - Transfers colors only in mid-tones (0.3 to 0.7)
- Selective: Highlights - Transfers colors only in bright regions (luminance > 0.7)
ColorCast's intuitive interface showing content image, style image, and result panels
Daltonize (P) mode correcting an image for protanopia, with correction intensity control
Install ColorCast using pip:
pip install colorcastFor GPU support (requires CUDA):
pip install colorcast[gpu]# Clone the repository
git clone https://github.com/MichailSemoglou/ColorCast.git
cd ColorCast
# Create a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install the package
pip install -e .
# For development with all dependencies
pip install -e ".[dev]"
# For GPU support (requires CUDA)
pip install -e ".[gpu]"- Python 3.10+
- NumPy >= 1.20.0
- scikit-image >= 0.19.0
- PyQt5 >= 5.15.0 (for GUI)
- scipy >= 1.7.0
See pyproject.toml for complete dependency specifications.
Run the graphical interface:
colorcast-guiOr use the package entry point:
python -m colorcastThe root colorcast.py script is kept as a deprecated compatibility shim and also launches the GUI. Use python -m colorcast or colorcast-gui for new work.
Step-by-step:
- Select Transfer Method: Choose from 9 different algorithms at the top
- Load Content Image: Click to select your base image
- Load Style Image: Click to select the image whose style you want to copy
- Apply Style Transfer: Click to process the images
- Adjust Intensity: Use the slider to control effect strength (0-100%)
- Save Result: Export your final image in your preferred format
- Clear Images: Reset and start over with new images
Basic color transfer:
colorcast transfer content.jpg style.jpg -o output.jpgWith method and intensity:
colorcast transfer content.jpg style.jpg -o output.jpg -m meanstd -i 0.7Batch process directory:
colorcast batch ./content_dir style.jpg -o ./output_dirList available methods:
colorcast list-methodsGet package information:
colorcast info --versionCLI Options:
-m, --method: Transfer method (histogram, meanstd, lab_reinhard, lut_linear, lut_scurve, lut_contrast, selective_shadows, selective_midtones, selective_highlights)-i, --intensity: Blend intensity 0.0-1.0-w, --workers: Number of parallel workers (default: 4)-p, --pattern: File pattern to match (default: *.jpg)
from colorcast import load_image, match_histograms_multichannel, save_image
# Load images
content = load_image("content.jpg")
style = load_image("style.jpg")
# Apply histogram matching
result = match_histograms_multichannel(content, style)
# Save result
save_image(result, "output.jpg")from colorcast import (
load_image,
color_transfer_meanstd,
color_transfer_lab,
lut_transfer_with_curve,
selective_color_transfer,
blend_images,
save_image,
)
content = load_image("content.jpg")
style = load_image("style.jpg")
# Mean/Std transfer
result1 = color_transfer_meanstd(content, style)
# Lab color transfer (Reinhard method)
result_lab = color_transfer_lab(content, style, alpha=0.8)
# LUT with S-curve
result2 = lut_transfer_with_curve(content, style, "s-curve")
# Selective shadows transfer
result3 = selective_color_transfer(content, style, mode="shadows")
# Blend with 70% intensity
final = blend_images(content, result2, intensity=0.7)
save_image(final, "output.jpg")from colorcast import load_image, registry
# List available methods
methods = registry.list_methods()
print(f"Available methods: {methods}")
# Get method and use it
content = load_image("content.jpg")
style = load_image("style.jpg")
method = registry.get_method("histogram")
result = method.transfer(content, style)from colorcast.processing.batch import BatchProcessor
from colorcast import match_histograms_multichannel
# Create batch processor
processor = BatchProcessor(
transfer_method=match_histograms_multichannel,
max_workers=4,
)
# Process directory
results = processor.process_directory(
content_dir="./content_images",
style_image="style.jpg",
output_dir="./output",
pattern="*.jpg",
)
# Check for failed files
if processor.failed_files:
print(f"Failed to process {len(processor.failed_files)} files")from colorcast.processing.cache import LRUCache
# Create cache
cache = LRUCache(max_size=100)
# Use cache for expensive operations
result = cache.get_or_compute(
key="transfer_key",
compute_func=lambda: match_histograms_multichannel(content, style),
)
# Get cache statistics
stats = cache.stats()
print(f"Cache hits: {stats['hits']}, misses: {stats['misses']}")- Histogram Matching: Best for artistic effects and dramatic color shifts
- Mean/Std Transfer: Better for subtle, natural-looking color grading
- Lab Color Transfer (Reinhard): Industry-standard method for professional color grading with perceptually uniform results
- LUT Curves: Experiment with different curves for varied contrast effects
- Linear: Standard transfer
- S-Curve: Enhanced midtones
- Contrast: Punchier overall look
- Selective Transfer: Target specific tonal ranges for precise control
- Shadows: Affect only dark areas
- Midtones: Affect only mid-tones (most natural for skin tones)
- Highlights: Affect only bright areas
- 0-30%: Very subtle color correction
- 30-60%: Natural color grading
- 60-80%: Noticeable style transfer
- 80-100%: Full style application
- Use images with similar aspect ratios for best results
- Higher resolution images produce better quality transfers
- For selective transfer, ensure good dynamic range in both images
Run the test suite:
# Run all tests
pytest
# Run with coverage
pytest --cov=colorcast --cov-report=html
# Run specific test file
pytest tests/test_transfer_methods.py
# Run with verbose output
pytest -vTest Coverage: 43% (162 passing tests, 1 skipped)
- Core transfer methods: 99% coverage
- Full test suite including integration, performance, and property-based tests
- Property-based tests require the optional
hypothesisdependency
1. Histogram Matching
matched = exposure.match_histograms(source, reference)- Per-channel histogram equalization
- Preserves complete color distribution
- Best for dramatic color transformations
2. Mean/Standard Deviation Transfer
result = ((source - μ_source) × (σ_ref / σ_source)) + μ_ref- Matches statistical properties per channel
- Better color balance for photographic work
3. Lab Color Space Transfer (Reinhard)
result_lab = ((source_lab - μ_source) × (σ_ref / σ_source)) + μ_ref- Operates in perceptually uniform Lab* color space
- Industry-standard in professional color grading
- Preserves color relationships better than RGB methods
- More natural-looking results
4. LUT with Curves
- Linear: Standard histogram matching
- S-Curve:
0.5 + 0.5 × sin(π(x - 0.5))- smooth midtone enhancement - Contrast:
x^0.8- power curve for increased punch
5. Selective Color Transfer
luminance = 0.299R + 0.587G + 0.114B
mask = (luminance >= shadow_threshold) & (luminance <= highlight_threshold)
result = source × (1 - mask) + matched × mask- Region-based masking using luminance
- Precise tonal range targeting
- LRU Cache: 10-20x speedup for repeated operations
- Batch Processing: Parallel processing with ThreadPoolExecutor
- Memory Efficient: Processes images in-place where possible
- Debounced Updates: 50ms delay prevents UI blocking
- Automatic Format Conversion: Grayscale → RGB, RGBA → RGB
ColorCast is perfect for:
- Film Color Grading - Matching scenes shot at different times
- Photography - Applying vintage or cinematic looks
- Art Creation - Transferring painting styles to photos
- Social Media Content - Creating consistent color themes
- Game Development - Style transfer for game assets
- Research - Academic research in color transfer
Contributions are welcome! Please feel free to:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
For development:
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run linting
black colorcast/
isort colorcast/
mypy colorcast/
# Run type checking
mypy colorcast/Released under the MIT License.
Michail Semoglou
- Email: m.semoglou@tongji.edu.cn
- Built with scikit-image for image processing
- GUI powered by PyQt5
- Performance optimized with NumPy
For issues, questions, or suggestions:

