Limitations of the Current Color Grading Implementation in video-use
The current color grading implementation in video-use restricts adjustments to three basic axes with hard-coded ±8% bounds, lacks LUT support, processes only per-clip rather than per-frame, and relies solely on CPU-based ffmpeg encoding without GPU acceleration.
The video-use repository provides a lightweight color grading utility located in helpers/grade.py designed for quick "clean-up" of standard footage. While functional for basic corrections, the implementation makes deliberate trade-offs that constrain creative flexibility and technical capability. Understanding these limitations of the current color grading implementation helps developers determine when to extend the tool or switch to professional grading suites.
Restricted Adjustment Axes and Creative Controls
The grader exposes only three adjustment parameters: contrast, gamma (brightness), and saturation. This restriction prevents sophisticated color work such as teal-orange grading, film emulation, or selective color replacement.
In helpers/grade.py, the preset definitions contain only eq= filters and a simple curves= option in the optional warm_cinematic preset (lines 38-63). The auto_grade_for_clip() function builds its filter string exclusively from contrast, gamma, and saturation values (lines 52-59). No mechanism exists for loading external LUTs (Look-Up Tables) or applying advanced curves adjustments.
Hard-Coded Bounds Prevent Strong Corrections
Every automatic adjustment is clamped within narrow ±8% limits, preventing strong creative looks or correction of extreme exposure issues.
The code explicitly constrains values using:
max(0.94, min(1.08, …))for contrastmax(0.94, min(1.10, …))for gammamax(0.94, min(1.06, …))for saturation
These clamps appear in lines 146-149 of helpers/grade.py. Even when manually specifying stronger adjustments via command line, the auto_grade_for_clip() function silently caps the values to these narrow ranges.
Per-Clip Processing Limitations
The implementation applies uniform corrections across entire clips, making it impossible to compensate for dynamic scenes with changing exposure or lighting conditions.
The auto_grade_for_clip() function returns a single filter string that gets applied to the complete input video (lines 84-91). There is no keyframe system or per-frame analysis capability to handle time-varying corrections.
Sampling Strategy Constraints
The automatic analysis relies on a sparse sampling method that may mischaracterize short or rapidly changing clips.
In _sample_frame_stats() (lines 98-100), the code calculates:
fps = max(0.5, min(n_samples / max(duration, 0.1), 10.0))
With n_samples hard-coded to 10 frames maximum and sampling rate capped at 10 fps, brief clips or scenes with quick transitions may receive inaccurate statistical analysis leading to suboptimal grades.
Color Space and Bit Depth Restrictions
The pipeline lacks modern color management capabilities, forcing all output to 8-bit YUV420P regardless of source material.
The ffmpeg command construction in apply_grade() hard-codes -pix_fmt yuv420p (lines 85-88), discarding chroma information from YUV-422 or BT.2020 sources. While the code parses bit depth via YBITDEPTH and normalizes using max_val = (2 ** bit_depth) - 1 (lines 158-162), the subsequent mathematics assumes 0-1 ranges that may under-represent high-dynamic-range material.
Additionally, if ffmpeg's signalstats filter cannot decode the source (common with HDR or unusual pixel formats), the system falls back to neutral defaults of y_mean=0.5 and sat_mean=0.25 (lines 154-157), effectively bypassing analysis.
Performance and Architectural Constraints
No GPU acceleration is available; all processing uses libx264 on the CPU with fixed settings.
The apply_grade() function always invokes -c:v libx264 -preset fast -crf 18 (lines 82-87), making no provision for hardware encoding (NVENC, Quick Sync, etc.) or GPU-accelerated filters. This limitation results in slow processing times for long or high-resolution clips.
The architecture also enforces a single-pass filter chain with no support for multi-pass color correction. The filter string consists of a single eq= chain without secondary curves= or colorbalance passes in auto mode (lines 51-58).
Practical Examples of Current Constraints
Attempting to apply the auto-grade reveals the narrow bounds in action:
python helpers/grade.py input.mp4 -o output.mp4
To inspect the generated filter and verify the clamping:
python helpers/grade.py --analyze input.mp4
# Example output:
# filter: eq=contrast=1.074:gamma=1.098:saturation=0.982
The preset selection is limited to three static options:
python helpers/grade.py input.mp4 -o graded.mp4 --preset warm_cinematic
Available presets (subtle, neutral_punch, warm_cinematic) are hard-coded in the PRESETS dictionary (lines 38-63). Adding creative looks requires editing helpers/grade.py directly; there is no runtime discovery of external LUT files.
Attempting to force stronger adjustments demonstrates the silent capping:
python helpers/grade.py input.mp4 -o out.mp4 --filter "eq=contrast=1.5:gamma=1.3:saturation=1.2"
# Values are clamped to ~1.08 maximum inside auto_grade_for_clip()
Summary
The video-use color grading system prioritizes simplicity over flexibility, resulting in significant technical constraints:
- Three-axis limitation: Only contrast, gamma, and saturation; no LUTs or color shifts
- ±8% hard caps: Preventing strong creative grades or extreme corrections
- Uniform per-clip application: No dynamic or per-frame adjustment capability
- Sparse sampling: Maximum 10 frames analyzed regardless of clip length
- 8-bit YUV420P output: Forces color space conversion and loses high-bit-depth information
- CPU-only processing: libx264 encoding without GPU acceleration options
- Static presets: No external LUT loading or runtime extensibility
These characteristics make the tool suitable for quick normalization of standard 8-bit SDR footage but inadequate for professional color work, HDR pipelines, or artistic grading.
Frequently Asked Questions
Can I apply custom LUTs with the current video-use grader?
No. The PRESETS dictionary in helpers/grade.py (lines 38-63) is hard-coded and contains no LUT loading mechanism. The auto-grade generates only eq= filter strings for contrast, gamma, and saturation. To use custom LUTs, you must manually extend the code to support ffmpeg's lut3d filter or preprocess footage externally.
Why is my color adjustment being capped at 8%?
The auto_grade_for_clip() function enforces hard limits using min() and max() clamps (lines 146-149): contrast is limited to 0.94-1.08, gamma to 0.94-1.10, and saturation to 0.94-1.06. These bounds prevent strong corrections and are applied silently even when manually specifying filter values.
Does video-use support HDR color grading?
No. While the code parses YBITDEPTH to normalize values (lines 158-162), the pipeline forces output to yuv420p (lines 85-88) and falls back to neutral defaults if ffmpeg's signalstats cannot decode HDR formats (lines 154-157). The mathematics assume SDR 0-1 ranges, making proper HDR grading impossible without significant modifications.
How can I process high-resolution videos faster with video-use?
Currently, you cannot leverage GPU acceleration. The apply_grade() function hard-codes -c:v libx264 with CPU encoding (lines 82-87). To improve performance, you would need to modify helpers/grade.py to support hardware encoders like NVENC (NVIDIA), VideoToolbox (Apple), or Quick Sync (Intel) by changing the codec parameters in the ffmpeg command construction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →