Skip to content

Commit

Permalink
video2rrd: new crate
Browse files Browse the repository at this point in the history
  • Loading branch information
astraw committed Jul 25, 2024
1 parent 5601b70 commit 9ec6082
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ members = [
"media-utils/show-timestamps",
"media-utils/strand-convert",
"media-utils/tiff-decoder",
"media-utils/video2rrd",
"mvg",
"mvg/mvg-util",
"nvenc",
Expand Down
28 changes: 28 additions & 0 deletions media-utils/video2rrd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "video2rrd"
description = "Convert video with Strand Cam timestamps to RRD format for Rerun Viewer"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.3.4", features = ["derive"] }
serde_yaml = "0.9"
color-eyre = "0.6.2"

tracing = "0.1.40"
rerun = "0.17.0"
opencv-ros-camera = "0.14.1"
cam-geom = "0.14.1"
nalgebra = "0.32.4"
regex = "1.10.3"
ndarray = "0.15.6"
machine-vision-formats = "0.1.1"
rayon = "1.9.0"
opencv = { version = "0.88.8", optional = true }

env-tracing-logger = { path = "../../env-tracing-logger" }
convert-image = { path = "../../convert-image" }
frame-source = { path = "../frame-source" }
basic-frame = { path = "../../basic-frame", features = ["convert-image"] }
mp4-writer = { path = "../mp4-writer" }
flydra-types = { path = "../../flydra-types" }
134 changes: 134 additions & 0 deletions media-utils/video2rrd/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use clap::Parser;
use color_eyre::eyre::{self, WrapErr};
use machine_vision_formats::{pixel_format, PixFmt};
use ndarray::Array;
use std::path::PathBuf;

use basic_frame::DynamicFrame;
use frame_source::{ImageData, Timestamp};

#[derive(Debug, Parser)]
#[command(version, about)]
struct Opt {
/// Input video filename.
#[arg(short, long)]
input: PathBuf,

/// Recording ID
#[arg(short, long)]
recording_id: Option<String>,

/// Entity Path
#[arg(short, long)]
entity_path: Option<String>,

/// Output rrd filename. Defaults to "<INPUT>.rrd"
#[arg(short, long)]
output: Option<PathBuf>,
}

fn to_rr_image(im: ImageData) -> eyre::Result<rerun::Image> {
let decoded = match im {
ImageData::Decoded(decoded) => decoded,
_ => eyre::bail!("image not decoded"),
};

if true {
// jpeg compression
let contents = basic_frame::match_all_dynamic_fmts!(
&decoded,
x,
convert_image::frame_to_image(x, convert_image::ImageOptions::Jpeg(80),)
)?;
let format = Some(rerun::external::image::ImageFormat::Jpeg);
Ok(rerun::Image::from_file_contents(contents, format).unwrap())
} else {
// Much larger file size but higher quality.
let w = decoded.width() as usize;
let h = decoded.height() as usize;

let image = match decoded.pixel_format() {
PixFmt::Mono8 => {
let mono8 = decoded.clone().into_pixel_format::<pixel_format::Mono8>()?;
Array::from_vec(mono8.into()).into_shape((h, w, 1)).unwrap()
}
_ => {
let rgb8 = decoded
.clone()
.into_pixel_format::<machine_vision_formats::pixel_format::RGB8>()?;
Array::from_vec(rgb8.into()).into_shape((h, w, 3)).unwrap()
}
};
Ok(rerun::Image::try_from(image)?)
}
}

fn main() -> eyre::Result<()> {
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "info");
}
env_tracing_logger::init();
let opt = Opt::parse();

let output = opt.output;

let output = output.unwrap_or_else(|| {
let mut output = opt.input.as_os_str().to_owned();
output.push(".rrd");
output.into()
});

let do_decode_h264 = true;
let mut src = frame_source::from_path(&opt.input, do_decode_h264)?;

let entity_path = if let Some(p) = opt.entity_path.as_ref() {
p.clone()
} else {
// get just the filename part
let movie_filename = opt
.input
.file_name()
.unwrap()
.to_os_string()
.to_str()
.unwrap()
.to_string();
movie_filename
};

tracing::info!("Frame size: {}x{}", src.width(), src.height());
let start_time = src.frame0_time().unwrap();
// let frametimes = self.frametimes.get(&cam_data.camn).unwrap();
// let (data2d_fnos, data2d_stamps): (Vec<i64>, Vec<f64>) = frametimes.iter().cloned().unzip();

// Initiate recording
let mut rec_builder = rerun::RecordingStreamBuilder::new(env!("CARGO_PKG_NAME"));

if let Some(recording_id) = opt.recording_id {
rec_builder = rec_builder.recording_id(recording_id);
}

let rec = rec_builder
.save(&output)
.wrap_err_with(|| format!("Creating output file {}", output.display()))?;

for frame in src.iter() {
let frame = frame?;
let pts = match frame.timestamp() {
Timestamp::Duration(pts) => pts,
_ => {
eyre::bail!("video has no PTS timestamps.");
}
};

let stamp_chrono = start_time + pts;
let stamp_flydra =
flydra_types::FlydraFloatTimestampLocal::<flydra_types::Triggerbox>::from(stamp_chrono);
let stamp_f64 = stamp_flydra.as_f64();
rec.set_time_seconds("wall_clock", stamp_f64);
let image = to_rr_image(frame.into_image())?;

rec.log(entity_path.as_str(), &image)?;
}
Ok(())
}

0 comments on commit 9ec6082

Please sign in to comment.