Metadata-Version: 2.4
Name: color-fx
Version: 0.1.0
Summary: Color grading library with LUT loading machinery, cinematic grades, and film-stock approximations
Project-URL: Homepage, https://github.com/tomastimelock/color-fx
Project-URL: Source, https://github.com/tomastimelock/color-fx
Project-URL: Issues, https://github.com/tomastimelock/color-fx/issues
Author-email: Trollfabriken AITrix AB <dev@trollfabriken.se>
License: MIT
License-File: LICENSE
Keywords: aces,cinematic,color-correction,color-grading,film-emulation,lut,opencolorio,post-production,vfx,video
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.10
Requires-Dist: ffmpeg-python>=0.2
Requires-Dist: numpy>=1.24
Requires-Dist: opencv-python-headless>=4.8
Requires-Dist: pillow>=10.0
Requires-Dist: pydantic>=2.5
Requires-Dist: typer>=0.9
Provides-Extra: aces
Requires-Dist: colour-science>=0.4; extra == 'aces'
Requires-Dist: opencolorio>=2.3; extra == 'aces'
Provides-Extra: all
Requires-Dist: colour-science>=0.4; extra == 'all'
Requires-Dist: opencolorio>=2.3; extra == 'all'
Requires-Dist: scikit-image>=0.22; extra == 'all'
Requires-Dist: scikit-learn>=1.3; extra == 'all'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-cov>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: sklearn
Requires-Dist: scikit-image>=0.22; extra == 'sklearn'
Requires-Dist: scikit-learn>=1.3; extra == 'sklearn'
Description-Content-Type: text/markdown

# color-fx

Color grading library with 60 grades, full LUT machinery, and optional ACES support.

[![PyPI](https://img.shields.io/pypi/v/color-fx)](https://pypi.org/project/color-fx/)
[![CI](https://github.com/tomastimelock/color-fx/actions/workflows/ci.yml/badge.svg)](https://github.com/tomastimelock/color-fx/actions/workflows/ci.yml)

---

## Install

```bash
pip install color-fx
```

That pulls in pydantic, numpy, Pillow, opencv-python-headless, and ffmpeg-python (~80 MB).
You also need **ffmpeg >= 6.0** on your PATH.

Extras:

| Extra | What it adds | Approx size |
|---|---|---|
| `pip install color-fx[sklearn]` | scikit-image + scikit-learn for KMeans skin-tone refinement | ~140 MB |
| `pip install color-fx[aces]` | colour-science + OpenColorIO for ACES and BT.2020 workflows | ~250 MB |
| `pip install color-fx[all]` | Both of the above | ~310 MB |

The base install works without either extra.

---

## 5-minute tour

Apply a named grade to a video:

```python
from color_fx import apply_grade, GradeConfig

apply_grade(
    video="raw.mp4",
    grade="nordic_desaturation_standard",
    output="graded.mp4",
    config=GradeConfig(
        grade="nordic_desaturation_standard",
        intensity_override=0.6,
        preserve_skin_tones=True,
    ),
)
```

Load and apply your own LUT:

```python
from color_fx import load_lut, apply_lut

lut = load_lut("/path/to/MyKodak2383.cube")
apply_lut(video="raw.mp4", lut=lut, output="graded.mp4", strength=0.8)
```

Chain multiple grades in one pass:

```python
from color_fx import apply_grade_chain

apply_grade_chain(
    video="raw.mp4",
    grades=[
        {"grade": "log_to_rec709"},
        {"grade": "warm_print_film_inspired"},
        {"grade": "shadow_lift", "config": {"intensity_override": 0.3}},
    ],
    output="layered.mp4",
)
```

---

## The 60 grades

Three categories. 60 slugs total.

### cinematic_grades (30)

Ten cinematic looks, each in three intensities: `subtle` (0.35), `standard` (0.70), `bold` (1.0).

| Slug | Display name |
|---|---|
| `nordic_desaturation_subtle` | Nordic Desaturation — Subtle |
| `nordic_desaturation_standard` | Nordic Desaturation — Standard |
| `nordic_desaturation_bold` | Nordic Desaturation — Bold |
| `golden_hour_subtle` | Golden Hour — Subtle |
| `golden_hour_standard` | Golden Hour — Standard |
| `golden_hour_bold` | Golden Hour — Bold |
| `cold_steel_subtle` | Cold Steel — Subtle |
| `cold_steel_standard` | Cold Steel — Standard |
| `cold_steel_bold` | Cold Steel — Bold |
| `warm_contrast_subtle` | Warm Contrast — Subtle |
| `warm_contrast_standard` | Warm Contrast — Standard |
| `warm_contrast_bold` | Warm Contrast — Bold |
| `muted_earth_subtle` | Muted Earth — Subtle |
| `muted_earth_standard` | Muted Earth — Standard |
| `muted_earth_bold` | Muted Earth — Bold |
| `high_contrast_bw_subtle` | High Contrast B&W — Subtle |
| `high_contrast_bw_standard` | High Contrast B&W — Standard |
| `high_contrast_bw_bold` | High Contrast B&W — Bold |
| `cyberpunk_neon_subtle` | Cyberpunk Neon — Subtle |
| `cyberpunk_neon_standard` | Cyberpunk Neon — Standard |
| `cyberpunk_neon_bold` | Cyberpunk Neon — Bold |
| `natural_flat_subtle` | Natural Flat — Subtle |
| `natural_flat_standard` | Natural Flat — Standard |
| `natural_flat_bold` | Natural Flat — Bold |
| `deep_shadows_subtle` | Deep Shadows — Subtle |
| `deep_shadows_standard` | Deep Shadows — Standard |
| `deep_shadows_bold` | Deep Shadows — Bold |
| `summer_fade_subtle` | Summer Fade — Subtle |
| `summer_fade_standard` | Summer Fade — Standard |
| `summer_fade_bold` | Summer Fade — Bold |

### film_stock_emulation (15)

Nine base film-stock approximations plus six cross-stock combination grades.

> **Film stock approximations** — entries in the `film_stock_emulation` category
> (Warm Print Film, Cool Cinematic Stock, Punchy Consumer Film, Grainy Black and White Stock,
> Instant Photo Vintage, Super 8 Film, 16mm Documentary, 35mm Prestige, Saturated Three-Strip Look,
> Expired Film, and the cross-stock combos) are **approximations** based on
> publicly-documented characteristics of named film stocks. They are NOT licensed or
> measured emulations. For production-grade film emulation requiring true spectral
> measurements, use commercial offerings (Kharma, FilmUnlimited, Cinematch).

| Slug | Display name |
|---|---|
| `warm_print_film_inspired` | Warm Print Film |
| `cool_cinematic_stock` | Cool Cinematic Stock |
| `punchy_consumer_film` | Punchy Consumer Film |
| `grainy_bw_stock` | Grainy Black and White Stock |
| `instant_photo_vintage` | Instant Photo Vintage |
| `super8_film` | Super 8 Film |
| `16mm_documentary` | 16mm Documentary |
| `35mm_prestige` | 35mm Prestige |
| `saturated_three_strip` | Saturated Three-Strip Look |
| `expired_film` | Expired Film |
| `cross_warm_cool` | Cross: Warm Print + Cool Cinematic |
| `cross_punchy_prestige` | Cross: Punchy Consumer + 35mm Prestige |
| `cross_super8_bw` | Cross: Super 8 + Grainy B&W |
| `cross_vintage_expired` | Cross: Instant Photo + Expired Film |
| `cross_documentary_prestige` | Cross: 16mm Documentary + 35mm Prestige |

### luts_and_curves (15)

Pure math operations: S-curves, log conversions, channel adjustments.

| Slug | Display name |
|---|---|
| `log_to_rec709` | Log to Rec.709 |
| `s_curve_gentle` | S-Curve (Gentle) |
| `s_curve_strong` | S-Curve (Strong) |
| `shadow_lift` | Shadow Lift |
| `highlight_rolloff` | Highlight Rolloff |
| `split_tone_warm_cool` | Split Toning: Warm Shadows / Cool Highlights |
| `split_tone_cool_warm` | Split Toning: Cool Shadows / Warm Highlights |
| `gamma_correction_rec709` | Gamma Correction (Rec.709) |
| `channel_mixer_warm` | Channel Mixer: Warm |
| `channel_mixer_cool` | Channel Mixer: Cool |
| `hsl_vibrance` | HSL Vibrance |
| `hsl_saturation_boost` | HSL Saturation Boost |
| `crush_blacks` | Crush Blacks |
| `lift_shadows_matte` | Lift Shadows (Matte) |
| `identity` | Identity (pass-through) |

---

## LUT loading

> **No bundled LUTs** — color-fx ships LUT *machinery* (read, apply, bake, interpolate)
> but does NOT ship any `.cube` files. Bring your own LUTs from free repositories
> (FreshLuts, Lutify.me free pack) or commercial vendors.

Read `.cube` or `.3dl` files:

```python
from color_fx import load_lut, save_lut, apply_lut, bake_grade_to_lut

lut = load_lut("/path/to/MyGrade.cube")    # or .3dl
print(lut.title, lut.size, lut.dimensions)  # e.g. "My Grade" 33 3

apply_lut(video="raw.mp4", lut=lut, output="out.mp4", strength=0.85)
```

Bake any named grade out to a `.cube` file for use in DaVinci Resolve or Premiere:

```python
bake_grade_to_lut(grade="nordic_desaturation_bold", output="nordic_bold.cube", size=33)
save_lut(lut, "custom.cube", title="My Custom Grade")
```

Convert between formats:

```python
from color_fx.lut import convert_lut
convert_lut(input="my_grade.3dl", output="my_grade.cube")
```

Both trilinear and tetrahedral 3D-LUT interpolation are available. Tetrahedral is the default
and is more accurate for most film-print-style grades.

ACES note: with `pip install color-fx[aces]`, you can specify `working_color_space="ACEScg"` in
`GradeConfig` and the library routes the transform through OpenColorIO automatically. Without the
extra, that field is accepted but ignored and the library processes in Rec.709.

---

## Engines

Three base engines cover all 60 grades:

| Engine | Module | Grades |
|---|---|---|
| `grade_recipe` | `engines/grade_recipe.py` | 30 cinematic grades (10 looks × 3 intensities) |
| `film_emulation` | `engines/film_emulation.py` | 15 film-stock approximations (9 base + 6 cross combos) |
| `curve_or_lut` | `engines/curve_or_lut.py` | 15 LUT and curve primitives |

Each grade recipe computes an internal 33x33x33 or 65x65x65 LUT at startup and caches it for the
duration of a render. The film emulation engine also drives a grain overlay and optional halation
simulation on top of the grade LUT.

---

## GradeConfig

All API functions accept an optional `GradeConfig`. Every field has a safe default.

| Field | Type | Default | Description |
|---|---|---|---|
| `grade` | `str` | — | Grade slug (required) |
| `intensity_override` | `float \| None` | `None` | Override catalog intensity, 0–1 |
| `region` | `tuple[int,int,int,int] \| None` | `None` | Apply only within `(x, y, w, h)` |
| `mask_image` | `str \| None` | `None` | Path to a greyscale mask image |
| `preserve_skin_tones` | `bool` | `False` | HSV-based skin-tone protection |
| `lut_strength` | `float` | `1.0` | Blend between identity (0) and full LUT (1) |
| `interpolation` | `"trilinear" \| "tetrahedral"` | `"tetrahedral"` | 3D LUT interpolation method |
| `working_color_space` | `"Rec.709" \| "Rec.2020" \| "ACES2065-1" \| "ACEScg"` | `"Rec.709"` | Requires `[aces]` for non-Rec.709 |
| `display_color_space` | `"Rec.709" \| "Rec.2020" \| "P3-D65"` | `"Rec.709"` | Output color space |
| `video_codec` | `str` | `"libx264"` | ffmpeg video codec |
| `audio_codec` | `str` | `"copy"` | ffmpeg audio handling |

The model uses `extra="forbid"`, so passing unknown fields raises a validation error at call time
rather than silently doing the wrong thing.

---

## CLI

All subcommands accept `--help`.

Apply a grade:

```bash
color-fx apply --video raw.mp4 --grade nordic_desaturation_standard --out graded.mp4
color-fx apply --video raw.mp4 --grade warm_print_film_inspired --intensity 0.5 --out out.mp4
```

Chain grades from a YAML config:

```bash
color-fx chain --video raw.mp4 --config chain.yaml --out out.mp4
```

LUT operations:

```bash
color-fx apply-lut --video raw.mp4 --lut my.cube --strength 0.8 --out graded.mp4
color-fx bake-lut --grade warm_print_film_inspired --size 33 --out warm_print.cube
color-fx convert-lut --input my.3dl --output my.cube
```

Catalog discovery:

```bash
color-fx list                               # all 60 grades, one slug per line
color-fx list --category cinematic_grades   # 30 grades in that category
color-fx list --include-skipped             # 60 + 15 video-fx-overlap stubs with redirect notes
color-fx categories                         # prints the 3 category names
color-fx info nordic_desaturation_bold      # full metadata, disclaimer if film stock
```

ACES (requires `[aces]` extra):

```bash
color-fx aces list
color-fx aces apply --video raw.mp4 --view "ACES 1.1 SDR Video" --out aces.mp4
```

Preview (generates a synthetic gradient video to inspect the grade):

```bash
color-fx preview --grade vintage_film_fade_standard --out preview.mp4
```

Schema (for LLM integration):

```bash
color-fx schema --pretty
```

---

## Composition

color-fx is a peer of optic-fx, video-fx, and cut-fx. It does not import any of them.

A typical pipeline:

```python
import subprocess

# 1. Cut and assemble with cut-fx / video-fx
# 2. Color grade with color-fx
from color_fx import apply_grade
apply_grade(video="assembled.mp4", grade="nordic_desaturation_standard", output="graded.mp4")

# 3. Add optical effects with optic-fx
from optic_fx import apply_effect
apply_effect(video="graded.mp4", effect="anamorphic_lens_flare_standard", output="final.mp4")
```

color-fx intentionally does NOT include Teal and Orange, Bleach Bypass, or other grades that
overlap with video-fx's `cinematic_color` category. Use `video_fx.apply_effect("teal_and_orange")`
for those. Running `color-fx list --include-skipped` shows each overlap slug and the exact
video-fx call to use instead.

---

## Disclaimers

**Film stock approximations** — entries in the `film_stock_emulation` category are approximations
based on publicly-documented characteristics of named film stocks. They are NOT licensed or
measured emulations. The library carries explicit disclaimer text in the catalog for every
film-stock entry; `get_grade_info()` and `color-fx info <slug>` surface this text at runtime.

**No bundled LUTs** — color-fx ships LUT machinery but does not ship any `.cube` files.

**ACES pathway** — the optional ACES OCIO config is NOT bundled in the wheel. It is downloaded
at first use when the `[aces]` extra is installed. Internet access is required the first time.

---

## What this is NOT

- Not a real-time preview tool. It processes video files via ffmpeg.
- Not a video editor or timeline tool. Use cut-fx or video-fx for that.
- Not a licensed film-emulation product. Film stocks are approximations only.
- Not a replacement for DaVinci Resolve's node-based pipeline. It is a Python library for
  programmatic and batch color grading.
- Not affiliated with or endorsed by any film stock manufacturer or camera brand.

---

## Provenance

Built at Trollfabriken AITrix AB for the CineForge pipeline cinematic grading work, AIMOS Insight
municipal report visual polish, SocKartan civic-transparency stylization, and the Timelock Film AB
festival slate's color-grading pass.

Source: https://github.com/tomastimelock/color-fx
PyPI: https://pypi.org/project/color-fx/
License: MIT
