首页 / 资讯中心 / 文章详情

FilmCraft Scopes:Lumetri 示波器面板背后的纯 Rust 数学引擎——波形、矢量示波器与 HDR PQ 轴实现详解

FilmCraft Scopes:Lumetri 示波器面板背后的纯 Rust 数学引擎——波形、矢量示波器与 HDR PQ 轴实现详解 ★ FEATURED ARTICLE
【免费下载链接】filmcraftAn open-source, clean-room reimplementation of Adobe Premiere Pro built in pure Rust.项目地址https://gitcode.com/gh_mirrors/fi/filmcraft点击查看免费下载本篇围绕filmcraft-scopes这一无 UI 的示波器计算 crate完整讲解它如何从一帧 RGB 信号出发计算出波形图Waveform、Parade、直方图Histogram与 YUV/HLS 矢量示波器Vectorscope五类示波器以及 HDR 场景下 BT.2408/PQ 亮度轴的换算方式。读完后你能掌握示波器计数网格这一中间表示的设计动机、各示波器的数学定义、Clamp Signal 与色彩矩阵的取值影响以及同一套计算如何同时服务 egui 面板绘制与 Agent 自动化的scopes.read数值接口。分层定位只有 math没有 UIcrates/scopes/README.md 对 crate 的定位一句话即可概括Video scope maths for the Lumetri Scopes panel (layer L3; depends only onfilmcraft-colorandserde; no UI, builds for wasm32)。依赖声明在 crates/scopes/Cargo.toml 中得到印证[dependencies]里只有filmcraft-color与serde两项没有任何图形、窗口或渲染依赖。从源码结构看这套计算与呈现分离的架构有三类消费者egui UI 面板crates/ui-egui/src/panels/scopes.rs 负责把 crate 算出的计数网格转成纹理并绘制刻度线、色标与面板外框README 原文即the UI draws what this crate computesAgent 自动化 APIcrates/engine/src/scopes.rs 中的scopes.read命令把示波器结果以数值百分比电平、直方图 bin、矢量示波器最密集点返回returns it as numbers for agents单元测试crates/scopes/src/tests.rs 用已知取值的生成帧做像素级断言。这种分层意味着 Web 端wasm32 目标也能复用同一套数学实现而 UI 只需关心怎么画。核心数据模型Signal 与参数Signal降采样到 480×270 的 RGB 码值一切计算的输入是Signal帧的 RGB 显示域码值display-encoded0…1 标称范围越界值代表 float/HDR 帧的超白/亚黑以行主序存放。README 强调降采样采用最近邻采样nearest-sample picking上限为 480 × 270对应源码中的两个常量/// Largest decimated signal (a 1080p frame is sampled every 4th pixel both ways). pub const MAX_W: usize 480; pub const MAX_H: usize 270;crates/scopes/src/lib.rs#L34-L36之所以不用平均降采样lib.rs模块注释给出了理由no averaging, so flat colours and test patterns land on exact cells——平坦色块与测试图样必须落在精确的网格单元上这是后面测试能断言代码值 c 落在第 c 行的前提。Signal的构造入口有三个对应 README 表格中的from_rgba8、from_rgba_f32_withlinear_to_pq、from_fn构造函数用途from_rgba8/from_rgba8_max8-bit 码值/255 归一化按MAX_W × MAX_H降采样见 crates/scopes/src/lib.rs#L312-L334from_rgba_f32_withfloat 像素经自定义映射如 linear → PQ后降采样HDR 通道使用from_fn合成信号测试与生成图案用另有一个Signal::map方法对整个信号做逐样本变换例如把 SDR 码值转到 PQ 轴见下文sdr_to_pq。降采样的步长由steps()用div_ceil计算1920×1080 的帧双向每 4 像素取 1 个得到恰好 480×270这一点被测试decimation_keeps_whole_samples直接验证crates/scopes/src/tests.rs#L173-L182。Params 与全套设置枚举计算参数集中在Paramspub struct Params { pub matrix: Matrix, // BT.601 / BT.709 / BT.2020 NCL pub clamp: bool, // Clamp Signal先钳到 0..1 再绘图 pub rows: usize, // 波形/Parade 的行数电平数 pub vector_size: usize, // 矢量示波器网格边长奇数保证中心是单元 } impl Default for Params { fn default() - Self { Params { matrix: Matrix::Bt709, clamp: true, rows: 256, vector_size: 255 } } }配合Params::range()可以直接读到电平轴的取值范围Clamp Signal 开启时行覆盖[0, 1]值先被钳制关闭时覆盖[-0.1, 1.1]越界值被丢弃而非钳制。README 特别指出一个精确对齐的性质256 行跨[0, 1]时8-bit 代码值c恰好落在第c行。README 中列出的全部设置类型均 serde、camelCase在源码中一一对应且每个枚举都提供了from_name宽松解析忽略大小写、空格与连字符便于 UI 菜单、JSON 参数与命令行互通类型取值说明源码位置ScopeKindvectorscopeYuv/vectorscopeHls/histogram/parade/waveform默认五种示波器vectorscope、yuv都解析为 YUV 矢量示波器见 crates/scopes/src/lib.rs#L49-L90WaveformTypergb默认/luma/yc/ycNoChromacrates/scopes/src/lib.rs#L97-L121ParadeTypergb默认/yuv/rgbWhitecrates/scopes/src/lib.rs#L124-L146ColorSpaceauto默认/rec601/rec709/rec2100Auto按序列解析HDR 工作空间 →Rec2100否则Rec709矩阵映射为 BT.601 / BT.709 / BT.2020 NCL见resolve与matrix()crates/scopes/src/lib.rs#L148-L189Scalebits8默认/float/hdr电平轴的刻度标签0–255 代码值 / 0.0–1.0 / cd/m²PQ 轴 0–10 000Brightnessdimmed/normal默认/bright增益分别为 0.55 / 1.0 / 1.6作用于轨迹亮度见 crates/scopes/src/lib.rs#L220-L246Targets75默认/100YUV 矢量示波器画哪组彩条目标amplitude()返回 0.75 / 1.0from_name的健壮性由names_round_trip测试覆盖709→Rec709、8 bit→Bits8、rgb-white→RgbWhite、Targets::Percent100序列化后是字符串100等crates/scopes/src/tests.rs#L267-L282。各示波器的数学定义统一表示计数网格 GridREADME 表格中反复出现count grid这一概念每种示波器最终都是一组计数网格——有多少个样本落在该单元。核心结构是Griddata[row * cols col]row 0 最低电平u32计数。波形与 Parade 是每条轨迹一张网格Waveform持有traces: VecGridlo/hi记录电平轴范围per_column记录每列样本数即信号高度。波形/Parade 共用同一个build()内核crates/scopes/src/lib.rs#L458-L479逐样本调用采样函数得到至多 4 个轨迹, 值对经row_of()映射到行号后累加计数越界值的处理由clamp开关控制钳制进轴内或按[-0.1, 1.1]判定丢弃。Waveform四种轨迹组合waveform()crates/scopes/src/lib.rs#L482-L495按类型产出不同轨迹集RGBR、G、B 三条网格Luma / YC no Chroma仅 Y 一条亮度 矩阵 YCbCr 的 Y 分量YCY 一条 C 一条其中 C 的轨迹在同一列上同时记录Y C与Y − C两个位置C √(Cb² Cr²)即色度幅度围绕亮度上下展开。测试yc_waveform_draws_chroma_around_luma用纯红帧验证Y 轨迹落在 Y 行C 轨迹落在Y ± C两行crates/scopes/src/tests.rs#L151-L162。Parade并排轨迹parade()crates/scopes/src/lib.rs#L497-L509与波形同构但轨迹是并排绘制的READMEdrawn side by sideRGBR、G、BRGB-WhiteR、G、B、Y 四条YUVY 原样、Cb 与 Cr 各加 0.5 偏移把 −0.5…0.5 的色度平移到 0…1 轴上测试yuv_parade_offsets_chroma_to_mid_scale验证灰色帧三条轨迹都落在行 128。UI 侧的并排拼接含gap在 crates/ui-egui/src/panels/scopes.rs 的parade_image()中完成。Histogram256 bin 与越界统计直方图结构Histogram持有 R、G、B、Y 各 256 个 bin另有below/above两组按通道计数的越界样本。bin_of()的映射规则是(v * 255).round()0 与 1 归入 0 与 255因此平坦色必然落在精确 bin——测试single_colour_lands_in_exact_histogram_bins中[200, 100, 50]的帧使h.r[200] h.g[100] h.b[50] 样本总数Y 通道则落在按矩阵公式算出的 Y 对应 bincrates/scopes/src/tests.rs#L33-L44。Clamp Signal 对直方图的影响值得注意越界样本始终计入below/above统计只有在 clamp 开启时才额外计入首/末 bincrates/scopes/src/lib.rs#L546-L560。测试clamp_signal_controls_out_of_range_values用含 1.2 / −0.05 的帧验证clamp 开时h.r[255] 8且h.above[0] 8clamp 关时h.r[255] 0crates/scopes/src/tests.rs#L184-L201。Vectorscope YUVCb 向右、Cr 向上、±0.6YUV 矢量示波器把每个样本的 (Cb, Cr) 画到vector_size²平面上。平面范围由常量VECTOR_EXTENT 0.6界定README 与 crates/scopes/src/lib.rs#L38-L39方向为Cb 向右、Cr 向上cell_of()中 fy 用0.5 - cr/extent*0.5反转 y 轴。该取值保证了所有矩阵的 100% 彩条目标都能落在平面内。彩条目标由targets(m, amplitude)生成六色 R、Mg、B、Cy、G、Yl在amplitude0.75 或 1.0下的码值过rgb_to_ycbcr得到 (Cb, Cr)crates/scopes/src/lib.rs#L685-L693。UI 用它画目标方框引擎测试则用它断言 75% 彩条帧代码值 191在 BT.601/709/2020 三个矩阵下每条彩条的样本恰好全部落在对应目标单元中心单元白 黑持有 2 倍样本数crates/scopes/src/tests.rs#L70-L105。两个与肤色线相关的常量SKIN_TONE_DEG 123.0NTSC I 轴方向自 Cb 逆时针 123°crates/scopes/src/lib.rs#L41-L42UI 的vector_graticule()沿此方向画一条肤色参考线red_angle(m)纯红在该矩阵矢量示波器上的角度测试断言 BT.709 为102.91°、BT.601 为108.65°crates/scopes/src/tests.rs#L108-L116且 123° 落在红色与黄色目标之间。网格落点的精确性也有已知答案255² 网格上BT.709 纯红峰位于(103, 21)这正是 README BT.709 red sits at (103, 21) on the 255² grid 一条的出处对应vectorscope_positions_of_primaries_are_known测试。Vectorscope HLS色相为角度、饱和度为半径HLS 矢量示波器是另一个坐标系crates/scopes/src/lib.rs#L639-L674角度 该矩阵 YUV 红色角 HSL 色相红 → 黄 → 绿 → 青 → 蓝 → 品红逆时针半径 HSL 饱和度 × 0.5饱和度 1 落在平面外缘 0.5 处。一个工程细节值得注意每样本的三角函数是热点实现预先对 0.1° 步长建了 3601 个方向向量表dirs按色相索引查表lib.rs注释明确写道 trigonometry per sample is the slow part。测试验证六种纯色恰好落在红 60° 步进的外环上半饱和红 [191,64,64] 落在半径约 50% 处灰色落在中心crates/scopes/src/tests.rs#L130-L149。hls_targets(m)则给出外环六色标签位置供 UI 标注。HDR 与 PQ 亮度轴README 的 Definitions 一节定义了 HDR 换算源码实现为三个函数crates/scopes/src/lib.rs#L377-L393linear_to_pqlinear 工作值1.0 203 cd/m²即 BT.2408输入是预乘 RGBA先除以 alpha→ SMPTE ST 2084 PQ 码值。轴上 0…1 对应0…10 000 cd/m²sdr_to_pqSDR 码值按BT.1886γ 2.4100 cd/m² 白换算到 PQ 轴——即 SDR 帧挂到 HDR 刻度时使用的路径nits_to_pqcd/m² → PQ 轴高度UI 画 cd/m² 刻度线0 / 10 / 100 / 203 / 1000 / 4000 / 10 000时逐点调用。测试hdr_axis_helpers给出三个锚点nits_to_pq(0) ≈ 0、nits_to_pq(10000) 1、参考白 203 cd/m² 对应 PQ 轴约58%半透明白预乘 0.5与不透明白换算结果相同sdr_to_pq(1.0)等于nits_to_pq(100)crates/scopes/src/tests.rs#L203-L213。Matrix枚举与 Kr/Kb 系数来自 crates/color/src/lib.rsBT.601 (0.299, 0.114)、BT.709 (0.2126, 0.0722)、BT.2020 NCL (0.2627, 0.0593)rgb_to_ycbcr按标准公式输出 Y ∈ 0…1、Cb/Cr ∈ −0.5…0.5。这就是 README 所说 YCbCr from RGB with the matrixs Kr/Kb … Cb and Cr in −0.5…0.5 的底层实现。从计数网格到像素paint 模块paint子模块crates/scopes/src/paint.rs把计数网格变成 UI 直接上传的 premultiplied RGBA8 纹理核心是intensity()的对数强度模型let r (1.0 reference.max(1.0)).ln(); (gain * (0.12 0.88 * (1.0 count as f32).ln() / r)).clamp(0.0, 1.0)reference参考计数是一个满单元的样本数波形取半列waveform_reference(per_column) per_column / 2平坦色块会占满整列矢量示波器取samples / 64。对数曲线让稀疏细节与平坦区域同时可见like a phosphor trace。多条轨迹加色叠加——R、G、B 轨迹重合处读出白色测试paint_adds_traces_and_leaves_empty_cells_transparent验证了 [128,128,128] 灰帧在行 128 输出[255,255,255,255]crates/scopes/src/tests.rs#L246-L264。矢量示波器另有vectorscope()光栅化每个点向3×3 邻域扩散中心权重 1.0、正交 0.6、对角 0.35取加权最大值保证小尺寸绘制时孤立点仍可见colorize模式下按该色度坐标反推颜色中亮度 Y0.6 的ycbcr_to_rgb再向白色去饱和测试断言 7 个不接触的彩条斑点恰好产生 7×9 个不透明像素。数值摘要给 Agent 的 summary 模块summary子模块crates/scopes/src/summary.rs回答不看图也能检查色彩的需求README 表格将其概括为levels / densest cells / coarse vectorscopechannel_stats直接对信号而非网格单元统计 R、G、B、Y 的 min/max/mean百分数注释类比 IRE8-bit 即 code/255×100外加平均色度 (Cb, Cr)−50…50、色相角与饱和度0.5 归一化。测试断言[200,100,50]的 BT.709 均值为 (78.43, 39.22, 19.61)trace_columns把一条轨迹按列切成buckets段各段输出所持有的电平区间如左右半黑白帧切成 4 段后得到 0/0、0–100、0–100、100/100peaks矢量示波器中样本占比 ≥min_share的最密集n个单元输出单元格坐标、(Cb, Cr) 百分数、角度、幅度100% 红在 BT.709 ≈ 102.6与占比彩条测试断言中心峰占比 0.25、六个目标各 0.125coarse把稀疏网格重分箱到size²如 16×16输出[x, y, count]提供粗略数值图。两个真实接口scopes.read 与 UI 面板scopes.read引擎自动化命令crates/engine/src/scopes.rs 把整个 crate 包成自动化命令scopes.read参数 schema 直接内嵌在命令注册处{scopes:[waveform,parade,histogram,vectorscopeYuv,vectorscopeHls]?, waveformType:rgb|luma|yc|ycNoChroma?,paradeType:rgb|yuv|rgbWhite?, colorSpace:auto|601|709|2100?,clamp:booltrue,columns:n8, peaks:n8,bins:booltrue,scale:0.5,time:ticks?|frame|seconds|timecode}关键实现细节scope_signalcrates/engine/src/scopes.rs#L15-L26SDR 路径以scale默认 0.5钳制在 1/32…1渲染监视器画面含字幕合成取 RGBA8 构造SignalHDR 路径colorSpace解析为Rec2100且序列工作空间为 HDR 时改用working_output: true的小比例工作空间渲染float 像素经scopes::linear_to_pq映射输出单位标记为pq%而非%时间参数time支持 ticks / frame 号 / 秒 / 时间码四种写法缺省取播放头并按帧率 snap输出 JSON 含帧坐标、samples: [w, h]、色彩空间/矩阵/clamp 回显、statschannel_stats以及各示波器结果波形/Parade 为按列分桶的电平区间直方图含below/above/peakBinbins: false可只留摘要矢量示波器为samples/mean/meanAngleDeg/peakspeaks1…64默认 8。UI 面板纹理缓存与刻度crates/ui-egui/src/panels/scopes.rs 展示了消费侧的完整闭环帧信号来自 ¼ 分辨率250px的程序监视帧缓存HDR 场景改走scope_signal(..., 0.125, true)frame_signal()以 (序列, 帧号, 修订号, pq 标志) 为键缓存新帧未渲染完时沿用上一帧纹理按 (帧, 设置, 示波器, 行数) 哈希缓存cached_texture只在帧或设置变化时重算——这与 README 末句 the UI recomputes only when the frame or a setting changes 对应电平刻度level_graticuleSDR 画 0…100右侧按 Scale 显示 0.0–1.0 或 0–255cd/m² 轴画 0/10/100/203/1000/4000/10000 七条线203 cd/m² 参考白用琥珀色高亮矢量刻度vector_graticule双环 十字轴 每 10° 刻度线YUV 模式画肤色线SKIN_TONE_DEG与 75%/100% 两组彩条目标HLS 模式画六色相标签hls_targets。正确性验证与性能预算测试套件crates/scopes/src/tests.rs全部基于已知取值的生成帧README 的 Tests 一节逐条对应平坦色精确 bin、256 级斜坡填满全部 bin 且波形列 x 落在行 x、75% 彩条在三个矩阵下同时验证 Parade 代码值与矢量目标单元、BT.709 红 (103, 21)/102.91° 与 BT.601 108.65°、HLS 外环 60° 步进、YC 色度包络亮度、Clamp Signal 双向行为、降采样整样本保持、NaN 与空帧不 panicnan_and_empty_signals_do_not_panic对 5 种示波器 × 2 种 clamp 组合跑全量compute、摘要数值与光栅化。性能方面README 给出cargo test --release -p filmcraft-scopes perf -- --ignored --nocapture1920×1080 RGBA8 噪声Apple M4 Pro单线程机器同时有其他构建在跑的实测数据步骤ms1080p → 480×270 降采样0.30波形 RGB / YC0.75 / 0.88Parade RGB-White0.67直方图0.35矢量示波器 YUV / HLS0.67 / 0.81每个示波器都远低于 3 ms 预算配合 UI 的帧/设置不变不重算策略示波器可以在播放中保持实时。性能测试本体perf_scopes_at_1080p使用 xorshift 伪随机噪声帧、20 次重复取均值crates/scopes/src/tests.rs#L284-L317。小结filmcraft-scopes展示了 NLE 示波器面板的一种干净实现方式把全部数学收敛到一个只依赖filmcraft-color与serde的纯计算层用计数网格这一与显示解耦的中间表示统一五种示波器用最近邻降采样保住 8-bit 精确对齐用对数强度 加色叠加还原荧光轨迹质感再用summary与paint分别服务数值接口和像素接口。其工程约束L3 层、wasm32 可构建、3 ms 预算都写进了 crates/scopes/README.md并逐条被 crates/scopes/src/tests.rs 的像素级断言与 ignored 性能测试所锚定是阅读该项目时理解面板 UI 从哪里拿到数据的最佳入口之一。赞分享【免费下载链接】filmcraftAn open-source, clean-room reimplementation of Adobe Premiere Pro built in pure Rust.项目地址https://gitcode.com/gh_mirrors/fi/filmcraft点击查看免费下载相关推荐TiXL 图像分析算子 WaveForm 完全指南波形示波器与矢量示波器叠加可视化TiXL 图像分析算子 WaveForm 完全指南波形示波器与矢量示波器叠加可视化 WaveForm 是 TiXLLib.image.analyze 库中音视频图形学桌面应用上一篇PyCOLMAP终极指南如何在Python中实现专业级3D重建下一篇如何使用Most Active GitHub Users Counter3分钟快速上手教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站