ffmpeg stuff

This commit is contained in:
Simon Gardling 2025-03-27 13:25:45 -04:00
parent 68e5d9fc3a
commit 278ccafb11
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
6 changed files with 91 additions and 116 deletions

5
.gitignore vendored
View File

@ -1,7 +1,6 @@
/target
/tmp
/test.mp4
/output.mp4
/.vscode
# CLion
**/.idea
**/.idea

4
Cargo.lock generated
View File

@ -297,9 +297,9 @@ checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a"
[[package]]
name = "once_cell"
version = "1.21.1"
version = "1.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b"
[[package]]
name = "physarum"

View File

@ -24,7 +24,7 @@ impl ThinGridData {
pub fn new_from_grid_vec(in_grids: &[Grid]) -> Vec<Self> {
in_grids
.iter()
.map(|grid| Self::new_from_grid(grid))
.map(Self::new_from_grid)
.collect()
}

View File

@ -2,7 +2,7 @@ mod agent;
mod blur;
mod buffer;
mod grid;
mod imgdata; // for storing image data
pub mod imgdata; // for storing image data
pub mod model;
mod palette;
mod util; // for math things

View File

@ -1,38 +1,61 @@
use physarum::model;
use physarum::{
imgdata::{ImgData, ThinGridData},
model,
};
use std::io::Write;
fn main() {
// # of iterations to go through
let n_iterations = 1024;
// let n_iterations = 2048;
// let n_iterations = 1 << 14;
// Size of grid and pictures
// let (width, height) = (256, 256);
// let (width, height) = (512, 512);
let (width, height) = (1024, 1024);
// # of agents
// let n_particles = 1 << 10;
// let n_particles = 1 << 16;
// let n_particles = 1 << 20;
let n_particles = 1 << 24;
println!("n_particles: {}", n_particles);
let n_particles = 1 << 22;
let diffusivity = 1;
// `n_populations` is the # of types of agents
// let n_populations = 4;
let n_populations = 1;
// let n_populations = 1 + rng.gen_range(1..4); // make # of populations between 2 and 5
let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity); // Create the model
let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity);
model.print_configurations();
model.print_configurations(); // Print config for model
// Setup ffmpeg
let mut ffmpeg = std::process::Command::new("ffmpeg")
.args(&[
"-y",
"-f",
"rawvideo",
"-pix_fmt",
"rgb24",
"-s",
&format!("{}x{}", width, height),
"-r",
"30",
"-i",
"-",
"-c:v",
"libx264",
"-preset",
"fast",
"-crf",
"23",
"output.mp4",
])
.stdin(std::process::Stdio::piped())
.spawn()
.expect("Failed to start ffmpeg");
let mut stdin = ffmpeg.stdin.take().unwrap();
model.run(n_iterations); // Actually run the model
for _ in 0..n_iterations {
model.step();
// export saved image data
println!("Rendering all saved image data....");
model.render_all_imgdata();
// Generate image
let grids = ThinGridData::new_from_grid_vec(model.population_grids());
let img_data = ImgData::new(grids, model.palette());
let img = img_data.to_image();
let raw_data = img.into_raw();
// Write to ffmpeg
stdin.write_all(&raw_data).unwrap();
}
// Cleanup
drop(stdin);
ffmpeg.wait().unwrap();
println!("Done!");
}

View File

@ -1,15 +1,14 @@
use crate::{
agent::Agent,
grid::{combine, Grid},
imgdata::{ImgData, ThinGridData},
palette::{random_palette, Palette},
};
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
use indicatif::{ProgressBar, ProgressStyle};
// use rand::Rng;
use rand_distr::{Distribution, Normal};
use rayon::{iter::ParallelIterator, prelude::*};
use std::{path::Path, time::Instant};
use std::time::Instant;
// Top-level simulation class.
pub struct Model {
@ -28,8 +27,8 @@ pub struct Model {
// Color palette
palette: Palette,
// List of ImgData to be processed post-simulation into images
img_data_vec: Vec<(usize, ImgData)>,
time_per_agent_list: Vec<f64>,
time_per_step_list: Vec<f64>,
}
impl Model {
@ -89,102 +88,56 @@ impl Model {
diffusivity,
iteration: 0,
palette: random_palette(),
img_data_vec: Vec::new(),
time_per_agent_list: Vec::new(),
time_per_step_list: Vec::new(),
}
}
// Simulates `steps` # of steps
#[inline]
pub fn step(&mut self) {
combine(&mut self.population_grids, &self.attraction_table);
let agents_tick_time = Instant::now();
self.population_grids.par_iter_mut().for_each(|grid| {
grid.tick();
grid.diffuse(self.diffusivity);
});
let agents_tick_elapsed = agents_tick_time.elapsed().as_millis() as f64;
let agents_num: usize = self.population_grids.iter().map(|g| g.agents.len()).sum();
let ms_per_agent = agents_tick_elapsed / agents_num as f64;
self.time_per_agent_list.push(ms_per_agent);
self.time_per_step_list.push(agents_tick_elapsed);
self.iteration += 1;
}
pub fn run(&mut self, steps: usize) {
let pb = ProgressBar::new(steps as u64);
pb.set_style(
ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})",
)
.progress_chars("#>-"),
);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})")
.progress_chars("#>-"));
let mut time_per_agent_list: Vec<f64> = Vec::new();
let mut time_per_step_list: Vec<f64> = Vec::new();
let agents_num: usize = self
.population_grids
.iter()
.map(|grid| grid.agents.len())
.sum();
(0..steps).for_each(|_| {
// Combine grids
combine(&mut self.population_grids, &self.attraction_table);
let agents_tick_time = Instant::now();
// Tick agents
self.population_grids.par_iter_mut().for_each(|grid| {
grid.tick();
grid.diffuse(self.diffusivity); // Diffuse + Decay
});
self.save_image_data();
let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64;
let ms_per_agent: f64 = agents_tick_elapsed / (agents_num as f64);
time_per_agent_list.push(ms_per_agent);
time_per_step_list.push(agents_tick_elapsed);
self.iteration += 1;
for _ in 0..steps {
self.step();
pb.inc(1);
});
}
pb.finish();
let avg_per_step: f64 =
time_per_step_list.iter().sum::<f64>() / time_per_step_list.len() as f64;
self.time_per_step_list.iter().sum::<f64>() / self.time_per_step_list.len() as f64;
let avg_per_agent: f64 =
time_per_agent_list.iter().sum::<f64>() / time_per_agent_list.len() as f64;
self.time_per_agent_list.iter().sum::<f64>() / self.time_per_agent_list.len() as f64;
println!(
"Average time per step: {}ms\nAverage time per agent: {}ms",
avg_per_step, avg_per_agent
);
}
fn save_image_data(&mut self) {
let grids = ThinGridData::new_from_grid_vec(&self.population_grids);
let img_data = ImgData::new(grids, self.palette);
self.img_data_vec.push((self.iteration + 1, img_data));
let size: usize = std::mem::size_of_val(&self.img_data_vec);
let mb = size / 1024 / 1024;
// println!("{} B | {} KB | {} MB", size, size/1024, size/1024/1024);
let max_mb = 6000;
if mb >= max_mb {
println!(
"ram usage is over {} MB (and len of {}), flushing to disk\n",
max_mb,
self.img_data_vec.len()
);
self.render_all_imgdata();
}
// Accessors for rendering
pub fn population_grids(&self) -> &[Grid] {
&self.population_grids
}
pub fn render_all_imgdata(&mut self) {
if !Path::new("./tmp").exists() {
std::fs::create_dir("./tmp").expect("could create directory");
}
let pb = ProgressBar::new(self.img_data_vec.len() as u64);
pb.set_style(ProgressStyle::default_bar().template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] ({pos}/{len}, {percent}%, {per_sec})",
));
self.img_data_vec
.drain(..)
.collect::<Vec<_>>()
.par_iter()
.progress_with(pb)
.for_each(|(i, img)| {
img.to_image()
.save(format!("./tmp/out_{}.png", i).as_str())
.unwrap();
});
pub fn palette(&self) -> Palette {
self.palette
}
}