pub struct ScopeAnalyzer;Expand description
Scope analysis utilities for decoded video frames.
All methods are associated functions (no instance state).
Implementations§
Source§impl ScopeAnalyzer
impl ScopeAnalyzer
Sourcepub fn waveform(frame: &VideoFrame) -> Vec<Vec<f32>>
pub fn waveform(frame: &VideoFrame) -> Vec<Vec<f32>>
Compute waveform monitor data for frame.
Returns a Vec of length frame.width(). Each inner Vec contains
the normalised Y (luma) values [0.0, 1.0] of every pixel in that column,
ordered top-to-bottom.
Only yuv420p, yuv422p, and yuv444p pixel formats are supported.
Returns an empty Vec for unsupported formats or if Y-plane data is
unavailable.
Sourcepub fn vectorscope(frame: &VideoFrame) -> Vec<(f32, f32)>
pub fn vectorscope(frame: &VideoFrame) -> Vec<(f32, f32)>
Compute vectorscope data for frame.
Returns a Vec of (cb, cr) pairs, one per chroma sample, with both
values normalised to [-0.5, 0.5].
Chroma dimensions vary by format:
yuv420p—(width/2) × (height/2)samplesyuv422p—(width/2) × heightsamplesyuv444p—width × heightsamples
Returns an empty Vec for unsupported formats or if chroma plane data
is unavailable.
Examples found in repository?
69fn main() {
70 let mut args = std::env::args().skip(1);
71 let mut input = None::<String>;
72 let mut interval_frames: u64 = 30;
73
74 while let Some(flag) = args.next() {
75 match flag.as_str() {
76 "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
77 "--interval" | "-n" => {
78 let raw = args.next().unwrap_or_default();
79 interval_frames = raw.parse().unwrap_or_else(|_| {
80 eprintln!("Invalid interval: {raw}");
81 process::exit(1);
82 });
83 }
84 other => {
85 eprintln!("Unknown flag: {other}");
86 process::exit(1);
87 }
88 }
89 }
90
91 let input = input.unwrap_or_else(|| {
92 eprintln!("Usage: scope_analyzer --input <video> [--interval <frames>]");
93 process::exit(1);
94 });
95
96 let mut decoder = match VideoDecoder::open(&input).build() {
97 Ok(d) => d,
98 Err(e) => {
99 eprintln!("Error opening video: {e}");
100 process::exit(1);
101 }
102 };
103
104 println!("Scope analysis: {input}");
105 println!("Sampling every {interval_frames} frame(s)");
106 println!();
107
108 let mut frame_count: u64 = 0;
109 let mut sample_count: u64 = 0;
110
111 loop {
112 let frame = match decoder.decode_one() {
113 Ok(Some(f)) => f,
114 Ok(None) => break,
115 Err(e) => {
116 eprintln!("Decode error: {e}");
117 process::exit(1);
118 }
119 };
120
121 if frame_count.is_multiple_of(interval_frames) {
122 let scatter = ScopeAnalyzer::vectorscope(&frame);
123 let parade = ScopeAnalyzer::rgb_parade(&frame);
124 print_vectorscope_summary(frame_count, &scatter);
125 print_rgb_parade_summary(frame_count, ¶de);
126 sample_count += 1;
127 }
128
129 frame_count += 1;
130 }
131
132 println!();
133 println!("Decoded {frame_count} frame(s), sampled {sample_count}.");
134}Sourcepub fn rgb_parade(frame: &VideoFrame) -> RgbParade
pub fn rgb_parade(frame: &VideoFrame) -> RgbParade
Compute RGB parade data for frame.
Each pixel is converted from YUV to RGB using the BT.601 full-range matrix
before sampling. Returns an RgbParade whose r, g, and b fields
each have the same column-major shape as ScopeAnalyzer::waveform.
Only yuv420p, yuv422p, and yuv444p pixel formats are supported.
Returns RgbParade { r: vec![], g: vec![], b: vec![] } for unsupported
formats or if plane data is unavailable.
Examples found in repository?
69fn main() {
70 let mut args = std::env::args().skip(1);
71 let mut input = None::<String>;
72 let mut interval_frames: u64 = 30;
73
74 while let Some(flag) = args.next() {
75 match flag.as_str() {
76 "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
77 "--interval" | "-n" => {
78 let raw = args.next().unwrap_or_default();
79 interval_frames = raw.parse().unwrap_or_else(|_| {
80 eprintln!("Invalid interval: {raw}");
81 process::exit(1);
82 });
83 }
84 other => {
85 eprintln!("Unknown flag: {other}");
86 process::exit(1);
87 }
88 }
89 }
90
91 let input = input.unwrap_or_else(|| {
92 eprintln!("Usage: scope_analyzer --input <video> [--interval <frames>]");
93 process::exit(1);
94 });
95
96 let mut decoder = match VideoDecoder::open(&input).build() {
97 Ok(d) => d,
98 Err(e) => {
99 eprintln!("Error opening video: {e}");
100 process::exit(1);
101 }
102 };
103
104 println!("Scope analysis: {input}");
105 println!("Sampling every {interval_frames} frame(s)");
106 println!();
107
108 let mut frame_count: u64 = 0;
109 let mut sample_count: u64 = 0;
110
111 loop {
112 let frame = match decoder.decode_one() {
113 Ok(Some(f)) => f,
114 Ok(None) => break,
115 Err(e) => {
116 eprintln!("Decode error: {e}");
117 process::exit(1);
118 }
119 };
120
121 if frame_count.is_multiple_of(interval_frames) {
122 let scatter = ScopeAnalyzer::vectorscope(&frame);
123 let parade = ScopeAnalyzer::rgb_parade(&frame);
124 print_vectorscope_summary(frame_count, &scatter);
125 print_rgb_parade_summary(frame_count, ¶de);
126 sample_count += 1;
127 }
128
129 frame_count += 1;
130 }
131
132 println!();
133 println!("Decoded {frame_count} frame(s), sampled {sample_count}.");
134}Sourcepub fn histogram(frame: &VideoFrame) -> Histogram
pub fn histogram(frame: &VideoFrame) -> Histogram
Compute a 256-bin histogram for each channel and for luminance.
For YUV frames luma is read directly from the Y plane; R, G, and B are
computed via BT.601 full-range conversion. Bins are indexed by the raw
8-bit value [0, 255].
Only yuv420p, yuv422p, and yuv444p pixel formats are supported.
Returns a zeroed Histogram for unsupported formats or if plane data
is unavailable.