Browse Source

theoretically the first working version

main
Thomas Johnson 11 months ago
parent
commit
fb2a48d31e
  1. 19
      src/main.rs
  2. 47
      src/map.rs

19
src/main.rs

@ -1,7 +1,6 @@
#![allow(confusable_idents)]
#![allow(mixed_script_confusables)]
use image::{ImageBuffer, Rgb, GenericImage};
use rayon::prelude::*;
mod transform;
mod genetic;
@ -15,7 +14,7 @@ fn main() {
let image_in_u16: ImageBuffer<Rgb<u16>, Vec<u16>> = image::open(args.get(1).expect("input file not provided")).expect("could not open input file").to_rgb16();
let out_name = args.get(2).expect("output file not provided");
let dims = image_in_u16.dimensions();
let mut image_in: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let image_in: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let pix = image_in_u16.get_pixel(x, y);
Rgb([pix[0] as f32 / 65535.0, pix[1] as f32 / 65535.0, pix[2] as f32 / 65535.0])
});
@ -23,21 +22,26 @@ fn main() {
let mut rng = rand::thread_rng();
let cfg = MapGeneticConfig::new(
image_in.clone(),
50,
100,
20,
0.1,
10,
);
let mut arena = genetic::Arena::<map::Map>::new(&mut rng, 100, 50, 50, cfg);
for i in 0..50 {
let mut arena = genetic::Arena::<map::Map>::new(&mut rng, 1000, 500, 500, cfg);
let mut i = 0;
while arena.set[0].error(&arena.config) > 50.0 {
println!("iteration {}", i);
arena.generate(&mut rng);
arena.par_process(|map, cfg| { map.cost(cfg); });
arena.par_cull();
let map = &arena.set[0];
println!("best cost: {}", map.cost(&arena.config));
println!("best cost and error: {}, {}", map.cost(&arena.config), map.error(&arena.config));
let last_map = arena.set.last().unwrap();
println!("worst cost and error: {}, {}", last_map.cost(&arena.config), last_map.error(&arena.config));
let mut image_in: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::from_fn(dims.0, dims.1, |_, _| Rgb([0.0, 0.0, 0.0]));
let mut image_out = image_in.clone();
for i in 0..20 {
for _ in 0..20 {
map.apply_add(&image_in, &mut image_out, dims);
// switch buffers; reuse as input
@ -51,6 +55,7 @@ fn main() {
Rgb([(pix[0] * scale) as u16, (pix[1] * scale) as u16, (pix[2] * scale) as u16])
});
image_out_u16.save(format!("{}{}.png", out_name, i)).expect("could not write output file");
i += 1;
}
// let map = cfg.create_map(&mut rng, 50);
// for i in 0..50 {

47
src/map.rs

@ -9,14 +9,14 @@ use image::{Rgb, GenericImage, ImageBuffer};
pub struct Map
{
transforms: Vec<Transform>,
cached_cost: RwLock<Option<f32>>,
cached_error: RwLock<Option<f32>>,
}
impl Clone for Map {
fn clone(&self) -> Self {
Self {
transforms: self.transforms.clone(),
cached_cost: RwLock::new(None),
cached_error: RwLock::new(None),
}
}
}
@ -36,8 +36,8 @@ impl Map {
}
}
pub fn cost(&self, cfg: &MapGeneticConfig) -> f32 {
let cached = { *self.cached_cost.read().unwrap() };
pub fn error(&self, cfg: &MapGeneticConfig) -> f32 {
let cached = { *self.cached_error.read().unwrap() };
if let Some(c) = cached {
c
} else {
@ -59,17 +59,21 @@ impl Map {
let variance = a_a / cfg.ndof as f32 - sum_a * sum_a / (cfg.ndof * cfg.ndof) as f32;
if denom == 0.0 || variance > 50.0 {
// if the image is entirely zero or it doesn't appear to converge, return infinite cost
*self.cached_cost.write().unwrap() = Some(f32::INFINITY);
*self.cached_error.write().unwrap() = Some(f32::INFINITY);
f32::INFINITY
} else {
let a_x = image_dot_product(&input, &cfg.goal_image);
let numer = a_x * a_x * cfg.ndof as f32 - 2.0 * a_x * sum_a * cfg.goal_sum + cfg.goal_sum * cfg.goal_sum * a_a;
let cost = cfg.goal_dot - numer / denom;
*self.cached_cost.write().unwrap() = Some(cost);
*self.cached_error.write().unwrap() = Some(cost);
cost
}
}
}
pub fn cost(&self, cfg: &MapGeneticConfig) -> f32 {
self.error(cfg) + self.transforms.len() as f32 * cfg.cost_per_tf
}
}
#[derive(Debug)]
@ -78,6 +82,8 @@ pub struct MapGeneticConfig
dims: (u32, u32),
pub initial_size: usize,
pub num_iters: usize,
pub mean_add: usize,
pub cost_per_tf: f32,
goal_image: ImageBuffer<Rgb<f32>, Vec<f32>>,
// dot product of goal image with itself
goal_dot: f32,
@ -85,7 +91,6 @@ pub struct MapGeneticConfig
goal_sum: f32,
// number of degrees of freedom (vector dimension)
ndof: usize,
}
impl Genetic for Map
@ -112,19 +117,31 @@ impl Genetic for Map
}
Map {
transforms: new_tfs,
cached_cost: RwLock::new(None),
cached_error: RwLock::new(None),
}
}
1 => {
let transform = cfg.random_transform(rng);
let mut new = self.clone();
new.transforms.push(transform);
let mut accum = 1.0;
let threshold = (cfg.mean_add as f32).exp().recip();
accum *= rng.gen::<f32>();
while accum > threshold {
let transform = cfg.random_transform(rng);
new.transforms.push(transform);
accum *= rng.gen::<f32>();
}
new
}
2 => {
let which = rng.gen_range(0..self.transforms.len());
let mut new = self.clone();
new.transforms.swap_remove(which);
let mut accum = 1.0;
let threshold = (cfg.mean_add as f32).exp().recip();
accum *= rng.gen::<f32>();
while new.transforms.len() != 0 && accum > threshold {
let which = rng.gen_range(0..new.transforms.len());
new.transforms.swap_remove(which);
accum *= rng.gen::<f32>();
}
new
}
_ => { unreachable!() }
@ -159,7 +176,7 @@ impl Genetic for Map
}
impl MapGeneticConfig {
pub fn new(goal_image: ImageBuffer<Rgb<f32>, Vec<f32>>, initial_size: usize, num_iters: usize) -> Self {
pub fn new(goal_image: ImageBuffer<Rgb<f32>, Vec<f32>>, initial_size: usize, num_iters: usize, cost_per_tf: f32, mean_add: usize) -> Self {
let dims = goal_image.dimensions();
let goal_dot = image_dot_product(&goal_image, &goal_image);
let goal_sum = image_sum(&goal_image);
@ -168,6 +185,8 @@ impl MapGeneticConfig {
dims,
initial_size,
num_iters,
cost_per_tf,
mean_add,
goal_image,
goal_dot,
goal_sum,
@ -182,7 +201,7 @@ impl MapGeneticConfig {
}
Map {
transforms,
cached_cost: RwLock::new(None),
cached_error: RwLock::new(None),
}
}

Loading…
Cancel
Save