M8: Fujifilm F-Log, F-Log2 and F-Log2 C colour spaces (F-Gamut / F-Gamut C)

Interpret Footage ▸ Color Management now offers "Fujifilm F-Log / F-Gamut", "Fujifilm F-Log2 /
F-Gamut" and "Fujifilm F-Log2 C / F-Gamut C" (X-S20, X-H2S, X-T5, GFX…), implemented from
Fujifilm's GFX ETERNA Technical Guide white paper v1.01 (the F-Log / F-Log2 data sheet
formulas): 10-bit code-value domain, F-Gamut = BT.2020 primaries, F-Gamut C primaries
(0.7347, 0.2653) (0.0263, 0.9737) (0.1173, -0.0224). Round trip and monotonicity tests cover
the new curves with the others; reference values from the white paper's table: 0 % → 95,
18 % → 470 / 400, 90 % → 705 / 570 (F-Log / F-Log2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
melchiaaronzedek
2026-10-22 19:32:05 +05:30
co-authored by Claude Opus 5.5
parent dd525c93d4
commit 6cfd42c511
4 changed files with 90 additions and 6 deletions
+2
View File
@@ -38,6 +38,8 @@ for limited- and full-range files.
| ARRI LogC4 | ARRI Wide Gamut 4 | ARRI, *ARRI LogC4 Logarithmic Color Space — Specification* (2022) | CV | a = (2¹⁸ − 16)/117.45, b = 928/1023, c = 95/1023, s = 7·ln2·2^(7 − 14c/b)/(a·b), t = (2^(6 − 14c/b) − 64)/a; y ≥ 0: x = (2^(14(y − c)/b + 6) − 64)/a; else x = y·s + t |
| Apple Log | BT.2020 | Apple, *Apple Log Profile* white paper (2023) | IRE | R0 = −0.05641088, Rt = 0.01, c = 47.28711236, β = 0.00964052, γ = 0.08550479, δ = 0.69336945; P ≥ c(Rt − R0)²: x = 2^((P − δ)/γ) − β; else x = √(P/c) + R0 |
| DJI D-Log | D-Gamut | DJI, *White Paper on D-Log and D-Gamut of DJI Cinema Color System* (2017) | CV | y ≤ 0.14: x = (y − 0.0929)/6.025; else x = (10^(3.89616y − 2.27752) − 0.0108)/0.9892 |
| Fujifilm F-Log | F-Gamut (= BT.2020 primaries) | Fujifilm, *GFX ETERNA Technical Guide – White Paper* v1.01 (2026), F-Log conversion formula (same as the F-Log data sheet) | CV | y ≥ cut2: x = 10^((y − d)/c)/a − b/a; else x = (y − f)/e; a 0.555556, b 0.009468, c 0.344676, d 0.790453, e 8.735631, f 0.092864, cut1 0.00089, cut2 0.100537775223865 |
| Fujifilm F-Log2 / F-Log2 C | F-Gamut / F-Gamut C (R 0.7347, 0.2653; G 0.0263, 0.9737; B 0.1173, −0.0224) | same document, F-Log2 C / F-Log2 conversion formula | CV | same form; a 5.555556, b 0.064829, c 0.245281, d 0.384316, e 8.799461, f 0.092864, cut1 0.000889, cut2 0.100686685370811 |
The encoders are the exact inverses given in the same documents. DJI **D-Log M** is not
implemented: DJI has not published its formula (only conversion LUTs), and we do not derive curves
+61 -2
View File
@@ -42,11 +42,25 @@ pub enum LogCurve {
AppleLog,
/// DJI D-Log.
DLog,
/// Fujifilm F-Log.
FLog,
/// Fujifilm F-Log2 (also the curve of F-Log2 C).
FLog2,
}
impl LogCurve {
pub const ALL: [LogCurve; 8] =
[LogCurve::SLog3, LogCurve::VLog, LogCurve::CLog2, LogCurve::CLog3, LogCurve::LogC3, LogCurve::LogC4, LogCurve::AppleLog, LogCurve::DLog];
pub const ALL: [LogCurve; 10] = [
LogCurve::SLog3,
LogCurve::VLog,
LogCurve::CLog2,
LogCurve::CLog3,
LogCurve::LogC3,
LogCurve::LogC4,
LogCurve::AppleLog,
LogCurve::DLog,
LogCurve::FLog,
LogCurve::FLog2,
];
pub fn domain(self) -> Domain {
match self {
@@ -65,6 +79,8 @@ impl LogCurve {
LogCurve::LogC4 => "ARRI LogC4",
LogCurve::AppleLog => "Apple Log",
LogCurve::DLog => "DJI D-Log",
LogCurve::FLog => "Fujifilm F-Log",
LogCurve::FLog2 => "Fujifilm F-Log2",
}
}
@@ -79,6 +95,8 @@ impl LogCurve {
LogCurve::LogC4 => logc4_encode(x),
LogCurve::AppleLog => apple_log_encode(x),
LogCurve::DLog => dlog_encode(x),
LogCurve::FLog => fujifilm_encode(&FLOG, x),
LogCurve::FLog2 => fujifilm_encode(&FLOG2, x),
}
}
@@ -93,6 +111,8 @@ impl LogCurve {
LogCurve::LogC4 => logc4_decode(y),
LogCurve::AppleLog => apple_log_decode(y),
LogCurve::DLog => dlog_decode(y),
LogCurve::FLog => fujifilm_decode(&FLOG, y),
LogCurve::FLog2 => fujifilm_decode(&FLOG2, y),
}
}
@@ -262,6 +282,35 @@ pub fn dlog_decode(y: f64) -> f64 {
if y <= 0.14 { (y - 0.0929) / 6.025 } else { (10f64.powf(3.896_16 * y - 2.277_52) - 0.0108) / 0.9892 }
}
// ---------------------------------------------------------------- Fujifilm F-Log / F-Log2
// Fujifilm, "GFX ETERNA Technical Guide - White Paper" v1.01 (2026), F-Log / F-Log2 C / F-Log2
// conversion formulas (the same as the F-Log and F-Log2 data sheets). `in` is scene-linear
// reflection, `out` 10-bit code value / 1023: 0 % → 95, 18 % → 470 (F-Log) / 400 (F-Log2).
/// a, b, c, d, e, f, cut1 (encoding: scene-linear), cut2 (decoding: signal).
struct Fujifilm {
a: f64,
b: f64,
c: f64,
d: f64,
e: f64,
f: f64,
cut1: f64,
cut2: f64,
}
const FLOG: Fujifilm =
Fujifilm { a: 0.555_556, b: 0.009_468, c: 0.344_676, d: 0.790_453, e: 8.735_631, f: 0.092_864, cut1: 0.000_89, cut2: 0.100_537_775_223_865 };
const FLOG2: Fujifilm =
Fujifilm { a: 5.555_556, b: 0.064_829, c: 0.245_281, d: 0.384_316, e: 8.799_461, f: 0.092_864, cut1: 0.000_889, cut2: 0.100_686_685_370_811 };
fn fujifilm_encode(k: &Fujifilm, x: f64) -> f64 {
if x >= k.cut1 { k.c * (k.a * x + k.b).log10() + k.d } else { k.e * x + k.f }
}
fn fujifilm_decode(k: &Fujifilm, y: f64) -> f64 {
if y >= k.cut2 { 10f64.powf((y - k.d) / k.c) / k.a - k.b / k.a } else { (y - k.f) / k.e }
}
#[cfg(test)]
mod tests {
use super::*;
@@ -317,6 +366,16 @@ mod tests {
// DJI D-Log: 0 → 0.0929, 18 % → 0.398 (white paper).
close(LogCurve::DLog.encode(0.0), 0.0929, 1e-9, "D-Log 0");
close(LogCurve::DLog.encode(0.18), 0.398, 0.002, "D-Log 18%");
// Fujifilm (white paper code-value table): 0 % → 95, 18 % → 470 / 400, 90 % → 705 / 570.
close(cv(LogCurve::FLog, 0.0), 95.0, 0.5, "F-Log 0%");
close(cv(LogCurve::FLog, 0.18), 470.0, 0.5, "F-Log 18%");
close(cv(LogCurve::FLog, 0.9), 705.0, 0.6, "F-Log 90%");
close(cv(LogCurve::FLog2, 0.0), 95.0, 0.5, "F-Log2 0%");
close(cv(LogCurve::FLog2, 0.18), 400.0, 0.5, "F-Log2 18%");
close(cv(LogCurve::FLog2, 0.9), 570.0, 0.5, "F-Log2 90%");
// the decoding cut is where the encoding's two pieces meet (within the published rounding)
close(LogCurve::FLog.encode(0.000_89), 0.100_537_775_223_865, 2e-4, "F-Log cut");
close(LogCurve::FLog2.encode(0.000_889), 0.100_686_685_370_811, 2e-4, "F-Log2 cut");
}
#[test]
+25 -2
View File
@@ -34,6 +34,8 @@ pub enum Gamut {
Awg4,
/// DJI D-Gamut.
DGamut,
/// Fujifilm F-Gamut C (F-Log2 C). F-Gamut itself has the BT.2020 primaries.
FGamutC,
}
impl Gamut {
@@ -51,6 +53,7 @@ impl Gamut {
Gamut::Awg3 => [[0.6840, 0.3130], [0.2210, 0.8480], [0.0861, -0.1020]],
Gamut::Awg4 => [[0.7347, 0.2653], [0.1424, 0.8576], [0.0991, -0.0308]],
Gamut::DGamut => [[0.71, 0.31], [0.21, 0.88], [0.09, -0.08]],
Gamut::FGamutC => [[0.7347, 0.2653], [0.0263, 0.9737], [0.1173, -0.0224]],
}
}
@@ -165,12 +168,18 @@ pub enum ColorSpace {
/// Apple Log (BT.2020 primaries).
AppleLog,
DLogDGamut,
/// Fujifilm F-Log / F-Gamut (BT.2020 primaries).
FLogFGamut,
/// Fujifilm F-Log2 / F-Gamut (BT.2020 primaries).
FLog2FGamut,
/// Fujifilm F-Log2 C / F-Gamut C.
FLog2CFGamutC,
/// Linear light with BT.709 primaries (EXR-style float sources).
Linear709,
}
impl ColorSpace {
pub const ALL: [ColorSpace; 16] = [
pub const ALL: [ColorSpace; 19] = [
ColorSpace::Rec709,
ColorSpace::Srgb,
ColorSpace::Rec2020,
@@ -186,6 +195,9 @@ impl ColorSpace {
ColorSpace::LogC4Awg4,
ColorSpace::AppleLog,
ColorSpace::DLogDGamut,
ColorSpace::FLogFGamut,
ColorSpace::FLog2FGamut,
ColorSpace::FLog2CFGamutC,
ColorSpace::Linear709,
];
@@ -207,6 +219,9 @@ impl ColorSpace {
ColorSpace::LogC4Awg4 => "logc4-awg4",
ColorSpace::AppleLog => "applelog",
ColorSpace::DLogDGamut => "dlog-dgamut",
ColorSpace::FLogFGamut => "flog-fgamut",
ColorSpace::FLog2FGamut => "flog2-fgamut",
ColorSpace::FLog2CFGamutC => "flog2c-fgamutc",
ColorSpace::Linear709 => "linear709",
}
}
@@ -228,6 +243,9 @@ impl ColorSpace {
ColorSpace::LogC4Awg4 => "ARRI LogC4 / ARRI Wide Gamut 4",
ColorSpace::AppleLog => "Apple Log / Rec. 2020",
ColorSpace::DLogDGamut => "DJI D-Log / D-Gamut",
ColorSpace::FLogFGamut => "Fujifilm F-Log / F-Gamut",
ColorSpace::FLog2FGamut => "Fujifilm F-Log2 / F-Gamut",
ColorSpace::FLog2CFGamutC => "Fujifilm F-Log2 C / F-Gamut C",
ColorSpace::Linear709 => "Linear (Rec. 709 primaries)",
}
}
@@ -242,7 +260,9 @@ impl ColorSpace {
pub fn gamut(self) -> Gamut {
match self {
ColorSpace::Rec709 | ColorSpace::Srgb | ColorSpace::Linear709 => Gamut::Bt709,
ColorSpace::Rec2020 | ColorSpace::Rec2100Pq | ColorSpace::Rec2100Hlg | ColorSpace::AppleLog => Gamut::Bt2020,
ColorSpace::Rec2020 | ColorSpace::Rec2100Pq | ColorSpace::Rec2100Hlg | ColorSpace::AppleLog | ColorSpace::FLogFGamut | ColorSpace::FLog2FGamut => {
Gamut::Bt2020
}
ColorSpace::P3D65 => Gamut::P3D65,
ColorSpace::SLog3SGamut3Cine => Gamut::SGamut3Cine,
ColorSpace::SLog3SGamut3 => Gamut::SGamut3,
@@ -251,6 +271,7 @@ impl ColorSpace {
ColorSpace::LogC3Awg3 => Gamut::Awg3,
ColorSpace::LogC4Awg4 => Gamut::Awg4,
ColorSpace::DLogDGamut => Gamut::DGamut,
ColorSpace::FLog2CFGamutC => Gamut::FGamutC,
}
}
@@ -269,6 +290,8 @@ impl ColorSpace {
ColorSpace::LogC4Awg4 => Curve::Log(LogCurve::LogC4),
ColorSpace::AppleLog => Curve::Log(LogCurve::AppleLog),
ColorSpace::DLogDGamut => Curve::Log(LogCurve::DLog),
ColorSpace::FLogFGamut => Curve::Log(LogCurve::FLog),
ColorSpace::FLog2FGamut | ColorSpace::FLog2CFGamutC => Curve::Log(LogCurve::FLog2),
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
# Roadmap detail
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-10 · **Change:** major (new file: milestones moved from ROADMAP.md, Current focus rewritten from the 2026-10-10 re-measure, beta milestones added) · **Target:** Adobe Premiere Pro 2026 (26.5.2)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-22 · **Change:** minor (M8: Fujifilm F-Log / F-Log2 / F-Log2 C colour spaces) · **Target:** Adobe Premiere Pro 2026 (26.5.2)
Forward-looking plan: milestones with status, the current focus and what's next, with estimates in
Opus 5.5 agent-hours (calibration in [target-app-parity.md](target-app-parity.md#calibration)).
@@ -68,7 +68,7 @@ Status: ✅ done · 🟡 in progress · ⬜ not started. Estimates are remaining
| M5 | Effects, keyframes, GPU | 🟡 | All 93 Premiere 26 video effects and 84 transitions (CPU), blend modes and 33 common effects on the GPU (#32, GPU2, GPU4, GPU5), keyframes + value/velocity graphs, wgpu compositor, effect + opacity masks (ellipse/polygon/pen, feather, expansion, modes; CPU/WGSL parity), mask tracking (Lucas–Kanade + RANSAC), adjustment layers, effect presets (built-in + user, JSON import/export) | More effects on the GPU (34 ids done incl. Lumetri basic / creative / vignette; GPU export landed #421), GPU masks in the live path, exact Warp Stabilizer (3-D) and Morph Cut | 40–70 |
| M6 | Export | ✅ | Own H.264 encoder (High/Main/Baseline, B-frames, VBR/CBR/2-pass) → MP4 or QuickTime + own AAC; ProRes, DNxHR, MJPEG, PNG/TIFF/BMP sequences (Premiere-style numbering), GIF, WAV, AIFF; background jobs; M6.5: Export mode parity (frame size / rate / scaling / pixel aspect, profile / level, CBR / VBR 1- and 2-pass, keyframe distance, audio codec / rate / channels / bitrate / sample size, multiplexer, burn-in or sidecar captions, image / name / timecode overlays, video limiter, loudness normalization with true-peak limiter, metadata, ranges, estimated size, summary), 24 built-in presets + user presets with favourites and import/export (Preset Manager), export queue (reorder, cancel, retry, several sequences / ranges), Quick Export; `export.*` commands and `filmcraft-cli export --preset` | Interlaced encoding, 5.1 audio, smart render, publishing destinations | 6–8 |
| M7 | Audio | 🟡 | Mixer graph (tracks → submixes → Mix, pre/post-fader inserts and sends, latency-compensated, sample-accurate, ~6× realtime for 24 tracks × 3 effects on one core), Audio Track Mixer + Audio Clip Mixer panels, track automation (Off/Read/Latch/Touch/Write, recorded live while playing, thinned to keyframes, timeline lanes with pen editing), solo/solo-safe, channel mapping basics, peak + BS.1770 loudness meters (match ffmpeg), DSP crate with 16 clip/track effects, Audio Gain (set/adjust/normalize), Constant Power / Constant Gain / Exponential Fade, Essential Sound (types, Loudness auto-match, Repair incl. DeEss/DeReverb, Clarity, Creative, Ducking keyframes, presets; full Dialogue chain 22× realtime) | Music duration remix, ML speech enhancement, 5.1 panner and multichannel buses, voice-over record, effect editor windows (EQ curve), remaining effects (multiband, convolution reverb), clip-mixer automation recording | 6–9 |
| M8 | Colour | 🟡 | Lumetri: basic, creative + looks, RGB & hue curves, wheels, HSL secondary, vignette, section bypass; Input LUT / Look LUT (.cube 1D/3D/shaper, .3dl; tetrahedral CPU + WGSL; project LUT library; built-in camera conversions); Colour Match (Oklab tonal-range statistics, skin protection, solved in Lumetri wheels); colour management: Rec. 709 / Rec. 2100 PQ / HLG working spaces + wide gamut, Interpret Footage colour space (S-Log3, V-Log, Canon Log 2/3, LogC3/4, Apple Log, D-Log from published specs), metadata auto-detect, BT.2390 tone mapping, gamut mapping, HDR export signalling (VUI/colr/mdcv/clli/SEI, ffprobe-verified); Lumetri Scopes: Vectorscope YUV (75/100 % targets, skin-tone line) and HLS, Histogram, Parade (RGB/YUV/RGB-White), Waveform (RGB/Luma/YC/YC no Chroma), presets, Rec. 601/709/2100, 8-bit/float/HDR (cd/m²) scales, multi-scope grid, `scopes.read` | HDR-aware Lumetri maths, macOS EDR monitors, mastering metadata → tone-map peak, D-Log M (no published formula), HSL Secondary refine | 3–5 |
| M8 | Colour | 🟡 | Lumetri: basic, creative + looks, RGB & hue curves, wheels, HSL secondary, vignette, section bypass; Input LUT / Look LUT (.cube 1D/3D/shaper, .3dl; tetrahedral CPU + WGSL; project LUT library; built-in camera conversions); Colour Match (Oklab tonal-range statistics, skin protection, solved in Lumetri wheels); colour management: Rec. 709 / Rec. 2100 PQ / HLG working spaces + wide gamut, Interpret Footage colour space (S-Log3, V-Log, Canon Log 2/3, LogC3/4, Apple Log, D-Log, Fujifilm F-Log / F-Log2 / F-Log2 C from published specs), metadata auto-detect, BT.2390 tone mapping, gamut mapping, HDR export signalling (VUI/colr/mdcv/clli/SEI, ffprobe-verified); Lumetri Scopes: Vectorscope YUV (75/100 % targets, skin-tone line) and HLS, Histogram, Parade (RGB/YUV/RGB-White), Waveform (RGB/Luma/YC/YC no Chroma), presets, Rec. 601/709/2100, 8-bit/float/HDR (cd/m²) scales, multi-scope grid, `scopes.read` | HDR-aware Lumetri maths, macOS EDR monitors, mastering metadata → tone-map peak, D-Log M (no published formula), HSL Secondary refine | 3–5 |
| M9 | More codecs | 🟡 | ProRes decode+encode, AAC decode+encode, HEVC Main/Main 10 decoder (bit-exact on 41 fixtures, ~225 fps 1080p), VP9 decoder (profiles 0–3, 8/10/12-bit, bit-exact on 50+ fixtures; WebM/MKV `V_VP9` and MP4 `vp09` import with key-frame-checked seeking), Matroska/WebM import (H.264/HEVC/VP9/ProRes/MJPEG + AAC/Opus/FLAC/MP3/Vorbis/PCM), Opus decoder (SILK/CELT/hybrid, 5.1/7.1 multistream; all RFC 8251 vectors range-exact; WebM/MKV/MP4), DNxHD/DNxHR decoder (all SMPTE ST 2019-1 CIDs, 8/10/12-bit, 4:2:2/4:4:4, interlaced, alpha; within IDCT precision of ffmpeg on 22 fixtures) + DNxHR LB/SQ/HQ/HQX/444 encoder and MOV `AVdh` export, AV1 decoder (Main profile, 8/10-bit, all intra/inter tools, loop filters, superres, film grain, intra BC, spatial layers; bit-exact vs libdav1d on 19 SVT fixtures + 22 libaom vectors; MP4 `av01` / WebM `V_AV1` import), MXF import (OP1a/OP-Atom; AVC with POC-ordered B pictures bit-exact, DNxHD/DNxHR, ProRes, PCM/AES3; MPEG-2 reported unsupported; index-table seeking, material package timecode), Ogg Opus/Vorbis (granule seeking, pre-skip, end trimming), image sequences, Broadcast WAV timecode | VP9 frame threading, AV1 threading/SIMD (~4 fps 1080p today), AV1 High/Professional profiles, H.264 High 10 / 4:2:2 and HEVC 4:2:2 in software (G3), E-AC-3, AVI (MPEG-2 decoder and hardware decode have landed) | 30–50 |
| M10 | Graphics & captions | 🟡 | Caption tracks (Subtitle/CEA-608/708/Teletext formats, track style), SRT/WebVTT/SCC import+export (frame-exact, property-tested), caption editing (add/split/merge/trim/move, sync-locked insert/extract), Text panel Captions tab, burn-in in Program monitor and export; text engine (`crates/text`: bundled + system fonts, harfrust shaping, bidi, line breaking, paragraph layout, glyph cache; 3-line 1080p title ≈ 0.15 ms warm); graphic clips with text + shape layers (fill, 2 strokes, background, shadow, keyframable transform), Type tool with on-monitor editing, shape/pen tools, Properties/Essential Graphics editor, align/distribute; graphics templates (own `.fcgt` format, 8 original built-ins, Browse tab with engine-rendered thumbnails, export / install / apply / editable properties; never reads .mogrt), rolls/crawls with ease and pre/postroll, responsive pins and intro/outro, per-character styles, Upgrade Caption to Graphic, source graphics, Replace Fonts; MCC (608+708), EBU STL, TTML/IMSC1, DFXP (frame-exact, property-tested) | 608/708 embedding in video streams, speech-to-text | 12–18 |
| M11 | Interchange & project management | ✅ | `.fcproj` schema versions + migrations, atomic saves, Save a Copy/Revert, auto-save ring + crash-recovery journal (Preferences ▸ Auto Save, recovery prompt), FCP7 XML, FCPXML, EDL, OTIO; offline media (own slate) + Link Media (fingerprint-checked relink, folder remap, search, Align Timecode, Make Offline); proxies (ProRes Proxy/LT, H.264 ¼/½ background jobs, attach/detach/reconnect, monitor toggle, export full-res) + ingest (copy/transcode/proxies); Project Manager (collect, consolidate + transcode with handles, size estimate) | Rename media to clip names, image-sequence conversion, Media Browser-driven relink, smart (cross-drive) path tracking | 1–2 |