Browse Source

switched to a heuristic for contractiveness; now it almost always converges to a fixpoint

main
Thomas Johnson 12 months ago
parent
commit
c966b2d72a
  1. 17
      src/genetic.rs
  2. 37
      src/main.rs
  3. 51
      src/map.rs
  4. 3
      src/transform.rs

17
src/genetic.rs

@ -1,22 +1,23 @@
use rand::Rng;
pub struct Arena<T: Genetic> {
pub keep: usize,
pub mutate: usize,
pub breed: usize,
pub crossover: usize,
set: Vec<T>,
pub config: T::Configuration,
}
impl<T: Genetic> Arena<T> {
pub fn new(keep: usize, mutate: usize, breed: usize, config: T::Configuration) -> Self {
pub fn new(keep: usize, mutate: usize, crossover: usize, config: T::Configuration) -> Self {
let mut set = Vec::new();
for _ in 0..keep {
set.push(T::generate(&config));
// set.push(T::generate(&config));
}
Arena {
keep,
mutate,
breed,
crossover,
set,
config,
}
@ -25,10 +26,10 @@ impl<T: Genetic> Arena<T> {
pub trait Genetic {
type Configuration;
fn generate(config: &Self::Configuration) -> Self;
fn mutate(&self, config: &Self::Configuration) -> Self;
fn breed(&self, other: &Self, config: &Self::Configuration) -> Self;
fn compare(&self, other: &Self, config: &Self::Configuration) -> std::cmp::Ordering;
fn generate<R: Rng>(rng: &mut R, config: &Self::Configuration) -> Self;
fn mutate<R: Rng>(&self, rng: &mut R, config: &Self::Configuration) -> Self;
fn crossover<R: Rng>(&self, rng: &mut R, other: &Self, config: &Self::Configuration) -> Self;
fn compare<R: Rng>(&self, rng: &mut R, other: &Self, config: &Self::Configuration) -> std::cmp::Ordering;
}

37
src/main.rs

@ -7,23 +7,46 @@ mod genetic;
mod map;
use crate::map::MapGeneticConfig;
use transform::Transform;
//use transform::Transform;
fn main() {
let args: Vec<_> = std::env::args().collect();
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 image_in: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let mut 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])
});
// 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: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::from_fn(dims.0, dims.1, |_, _| Rgb([0.0, 0.0, 0.0]));
let mut rng = rand::thread_rng();
let mut cfg = MapGeneticConfig {
rng: rand::thread_rng(),
dims,
initial_size: 50,
};
let map = cfg.create_map(50);
println!("map: {:?}", map);
let map = cfg.create_map(&mut rng, 50);
for i in 0..75 {
map.apply_add(&image_in, &mut image_out, dims);
if i % 25 == 0 {
// write intermediate to file
let mut image_out_file = image_out.clone();
correct_image_brightness(&mut image_out_file, dims, 0.5, 1.0);
let image_out_u16: ImageBuffer<Rgb<u16>, Vec<u16>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let pix = image_out.get_pixel(x, y);
let scale = 65535.0;
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");
}
// switch buffers; reuse as input
core::mem::swap(&mut image_out, &mut image_in);
image_out = ImageBuffer::from_fn(dims.0, dims.1, |_, _| Rgb([0.0, 0.0, 0.0]));
}
map.apply_add(&image_in, &mut image_out, dims);
// let transform = Transform {
// color_matrix: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
@ -34,13 +57,13 @@ fn main() {
// to_corner: (0.0, 1.0),
// to_extents: [(1.0, -0.5), (0.0, -0.5)],
// };
correct_image_brightness(&mut image_out, dims, 0.5, 0.01);
correct_image_brightness(&mut image_out, dims, 0.5, 0.15);
let image_out_u16: ImageBuffer<Rgb<u16>, Vec<u16>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let pix = image_out.get_pixel(x, y);
let scale = 65535.0;
Rgb([(pix[0] * scale) as u16, (pix[1] * scale) as u16, (pix[2] * scale) as u16])
});
image_out_u16.save(args.get(2).expect("output file not provided")).expect("could not write output file");
image_out_u16.save(format!("{}.png", out_name)).expect("could not write output file");
}
fn correct_image_brightness<I: GenericImage<Pixel=Rgb<f32>>>(img: &mut I, dims: (u32, u32), mean: f32, variance: f32) {

51
src/map.rs

@ -1,73 +1,72 @@
use crate::{transform::{Transform, NCOLORS}, genetic::Genetic};
use core::fmt::Debug;
use rand::{Rng, distributions::{Uniform, WeightedIndex}};
use rand_distr::{Distribution, Poisson};
use rand_distr::{Distribution};
use image::{Rgb, GenericImage};
#[derive(Debug)]
pub struct Map<R>
pub struct Map
{
transforms: Vec<Transform>,
pd: core::marker::PhantomData<R>,
}
impl<R: Rng> Map<R> {
impl Map {
pub fn apply_add<I: GenericImage<Pixel=Rgb<f32>>, O: GenericImage<Pixel=Rgb<f32>>>(&self, input: &I, output: &mut O, dims: (u32, u32)) {
let offset = 1.0;//((dims.0 * dims.1 * NCOLORS as u32) as f32).sqrt().recip();
for tf in self.transforms.iter() {
tf.apply_add(input, output, dims);
}
for x in 0..dims.0 {
for y in 0..dims.1 {
let Rgb([r, g, b]) = output.get_pixel(x, y);
output.put_pixel(x, y, Rgb([r + 1.0, g + 1.0, b + 1.0]));
output.put_pixel(x, y, Rgb([r + offset, g + offset, b + offset]));
}
}
}
}
#[derive(Debug)]
pub struct MapGeneticConfig<R: Rng + Debug>
pub struct MapGeneticConfig
{
pub rng: R,
pub dims: (u32, u32),
pub initial_size: usize,
}
impl<R: Rng + Debug> Genetic for Map<R>
impl Genetic for Map
{
type Configuration = MapGeneticConfig<R>;
type Configuration = MapGeneticConfig;
fn generate(cfg: &MapGeneticConfig<R>) -> Self {
unimplemented!()
fn generate<R: Rng>(rng: &mut R, cfg: &Self::Configuration) -> Self {
cfg.create_map(rng, cfg.initial_size)
}
fn mutate(&self, cfg: &MapGeneticConfig<R>) -> Self {
fn mutate<R: Rng>(&self, _rng: &mut R, _cfg: &Self::Configuration) -> Self {
unimplemented!()
}
fn breed(&self, other: &Self, config: &Self::Configuration) -> Self {
fn crossover<R: Rng>(&self, _rng: &mut R, _other: &Self, _config: &Self::Configuration) -> Self {
unimplemented!()
}
fn compare(&self, other: &Self, config: &Self::Configuration) -> std::cmp::Ordering {
fn compare<R: Rng>(&self, _rng: &mut R, _other: &Self, _config: &Self::Configuration) -> std::cmp::Ordering {
unimplemented!()
}
}
impl<R: Rng + Debug> MapGeneticConfig<R> {
pub fn create_map(&mut self, tf_count: usize) -> Map<R> {
impl MapGeneticConfig {
pub fn create_map<R: Rng>(&self, rng: &mut R, tf_count: usize) -> Map {
let mut transforms = vec![];
for _ in 0..tf_count {
transforms.push(self.random_transform(1.0));
transforms.push(self.random_transform(rng, (tf_count as f32).recip().sqrt() * 3.5));
}
Map {
transforms,
pd: core::marker::PhantomData,
}
}
pub fn random_transform(&mut self, c: f32) -> Transform {
pub fn random_transform<R: Rng>(&self, mut rng: &mut R, c: f32) -> Transform {
let rect_xs = loop {
let pair = (self.rng.gen_range(0..self.dims.0), self.rng.gen_range(0..self.dims.0));
let pair = (rng.gen_range(0..self.dims.0), rng.gen_range(0..self.dims.0));
if pair.1 < pair.0 {
break (pair.1, pair.0);
} else if pair.0 < pair.1 {
@ -75,26 +74,26 @@ impl<R: Rng + Debug> MapGeneticConfig<R> {
}
};
let rect_ys = loop {
let pair = (self.rng.gen_range(0..self.dims.1), self.rng.gen_range(0..self.dims.1));
let pair = (rng.gen_range(0..self.dims.1), rng.gen_range(0..self.dims.1));
if pair.1 < pair.0 {
break (pair.1, pair.0);
} else if pair.0 < pair.1 {
break pair;
}
};
let mut corner_dist = Uniform::new(0.0, 1.0).sample_iter(&mut self.rng);
let mut corner_dist = Uniform::new(0.0, 1.0).sample_iter(&mut rng);
let (cx, cy) = (corner_dist.next().unwrap(), corner_dist.next().unwrap());
let mut off_dist = Uniform::new(-0.5, 0.5).sample_iter(&mut self.rng);
let mut off_dist = Uniform::new(-0.5, 0.5).sample_iter(&mut rng);
let (e1x, e1y, e2x, e2y) = (off_dist.next().unwrap(), off_dist.next().unwrap(), off_dist.next().unwrap(), off_dist.next().unwrap());
let (to_corner, to_extents) = match WeightedIndex::new(&[1, 1, 1, 1]).unwrap().sample(&mut self.rng) {
let (to_corner, to_extents) = match WeightedIndex::new(&[1, 1, 1, 1]).unwrap().sample(rng) {
0 => ((cx, cy), [(e1x, e1y), (e2x, e2y)]),
1 => ((cx + e1x, cy + e1y), [(e2x, e2y), (-e1x, -e1y)]),
2 => ((cx + e1x + e2x, cy + e1y + e2y), [(-e1x, -e1y), (-e2x, -e2y)]),
3 => ((cx + e2x, cy + e2y), [(-e2x, -e2y), (e1x, e1y)]),
_ => unreachable!(),
};
let color_matrix_bound = ((self.dims.0 * self.dims.1) as f32).sqrt().recip() * c;
let mut matrix_coeff_dist = Uniform::new(-color_matrix_bound, color_matrix_bound).sample_iter(&mut self.rng);
let color_matrix_bound = /*((self.dims.0 * self.dims.1 * NCOLORS as u32) as f32).sqrt().recip() * */c;
let mut matrix_coeff_dist = Uniform::new(-color_matrix_bound, color_matrix_bound).sample_iter(rng);
let mut color_matrix = [0.0f32; NCOLORS * NCOLORS];
for i in 0..NCOLORS * NCOLORS {
color_matrix[i] = matrix_coeff_dist.next().unwrap();

3
src/transform.rs

@ -1,5 +1,4 @@
use image::{Rgb, GenericImage};
use rand::Rng;
pub const NCOLORS: usize = 3;
@ -24,7 +23,6 @@ impl Transform {
let in_coords = (x_in, y_in);
// Get input color from pixels near sample
let mut total_area = 0.0;
let mut in_pix = [0.0; NCOLORS];
let in_coords_i32 = (in_coords.0.floor() as i32, in_coords.1.floor() as i32);
for x in in_coords_i32.0..=in_coords_i32.0 + 1 {
@ -35,7 +33,6 @@ impl Transform {
let val = input.get_pixel(x as u32, y as u32).0;
let frac = (x as f32 - in_coords.0, y as f32 - in_coords.1);
let area = (1.0 - frac.0.abs()) * (1.0 - frac.1.abs());
total_area += area;
for i in 0..NCOLORS {
in_pix[i] += val[i] * area;
}

Loading…
Cancel
Save