Browse Source

got it making abstract art or something

main
Thomas Johnson 12 months ago
parent
commit
1f0939efb6
  1. 42
      src/main.rs
  2. 88
      src/map.rs
  3. 13
      src/transform.rs

42
src/main.rs

@ -1,11 +1,12 @@
#![allow(confusable_idents)]
#![allow(mixed_script_confusables)]
use image::{ImageBuffer, Rgb};
use image::{ImageBuffer, Rgb, GenericImage};
mod transform;
mod genetic;
mod map;
use crate::map::MapGeneticConfig;
use transform::Transform;
fn main() {
@ -17,16 +18,23 @@ fn main() {
Rgb([pix[0] as f32 / 65535.0, pix[1] as f32 / 65535.0, pix[2] as f32 / 65535.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 transform = Transform {
color_matrix: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
from_corner: (0, 0),
from_extents: dims,
// to_corner: (0.0, 0.0),
// to_extents: [(dims.0 as f32, 0.0), (0.0, dims.1 as f32)],
to_corner: (0.0, dims.1 as f32),
to_extents: [(dims.0 as f32, dims.1 as f32 * -0.5), (0.0, dims.1 as f32 * -0.5)],
let mut cfg = MapGeneticConfig {
rng: rand::thread_rng(),
dims,
};
transform.apply_add(&image_in, &mut image_out, dims);
let map = cfg.create_map(50);
println!("map: {:?}", map);
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],
// from_corner: (0, 0),
// from_extents: dims,
//// to_corner: (0.0, 0.0),
//// to_extents: [(dims.0 as f32, 0.0), (0.0, dims.1 as f32)],
// 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);
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;
@ -34,3 +42,17 @@ fn main() {
});
image_out_u16.save(args.get(2).expect("output file not provided")).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) {
// add row-wise for numerical stability
let current_mean = (0..dims.1).map(|y| (0..dims.0).map(|x| { let c = img.get_pixel(x, y); c.0[0] as f64 + c.0[1] as f64 + c.0[2] as f64 }).sum::<f64>()).sum::<f64>() / (3 * dims.0 * dims.1) as f64;
let current_variance_recip = (3 * dims.0 * dims.1) as f64 / (0..dims.1).map(|y| (0..dims.0).map(|x| { let c = img.get_pixel(x, y); let (r, g, b) = (c.0[0] as f64 - current_mean, c.0[1] as f64 - current_mean, c.0[2] as f64 - current_mean); r * r + g * g + b * b }).sum::<f64>()).sum::<f64>();
println!("{} {}", current_mean, current_variance_recip);
let correct = |x| (x - current_mean as f32) * current_variance_recip as f32 * variance + mean;
for x in 0..dims.0 {
for y in 0..dims.1 {
let Rgb([r, g, b]) = img.get_pixel(x, y);
img.put_pixel(x, y, Rgb([correct(r), correct(g), correct(b)]));
}
}
}

88
src/map.rs

@ -1,26 +1,38 @@
use crate::{transform::Transform, genetic::Genetic};
use rand::{Rng, distributions::Uniform};
use rand_distr::{Poisson};
use crate::{transform::{Transform, NCOLORS}, genetic::Genetic};
use core::fmt::Debug;
use rand::{Rng, distributions::{Uniform, WeightedIndex}};
use rand_distr::{Distribution, Poisson};
use image::{Rgb, GenericImage};
pub struct Map<R: rand::Rng>
#[derive(Debug)]
pub struct Map<R>
{
transforms: Vec<Transform>,
pd: core::marker::PhantomData<R>,
}
pub fn random_transform<R: rand::Rng>(cfg: &mut MapGeneticConfig<R>) -> Transform {
unimplemented!()
impl<R: Rng> Map<R> {
pub fn apply_add<I: GenericImage<Pixel=Rgb<f32>>, O: GenericImage<Pixel=Rgb<f32>>>(&self, input: &I, output: &mut O, dims: (u32, u32)) {
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]));
}
}
}
}
pub struct MapGeneticConfig<R: rand::Rng>
#[derive(Debug)]
pub struct MapGeneticConfig<R: Rng + Debug>
{
pub rng: R,
pub dims: (u32, u32),
pub add_mean: usize,
pub rm_mean: usize,
}
impl<R: rand::Rng> Genetic for Map<R>
impl<R: Rng + Debug> Genetic for Map<R>
{
type Configuration = MapGeneticConfig<R>;
@ -40,3 +52,59 @@ impl<R: rand::Rng> Genetic for Map<R>
unimplemented!()
}
}
impl<R: Rng + Debug> MapGeneticConfig<R> {
pub fn create_map(&mut self, tf_count: usize) -> Map<R> {
let mut transforms = vec![];
for _ in 0..tf_count {
transforms.push(self.random_transform(1.0));
}
Map {
transforms,
pd: core::marker::PhantomData,
}
}
pub fn random_transform(&mut self, 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));
if pair.1 < pair.0 {
break (pair.1, pair.0);
} else if pair.0 < pair.1 {
break pair;
}
};
let rect_ys = loop {
let pair = (self.rng.gen_range(0..self.dims.1), self.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 (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 (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) {
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 mut color_matrix = [0.0f32; NCOLORS * NCOLORS];
for i in 0..NCOLORS * NCOLORS {
color_matrix[i] = matrix_coeff_dist.next().unwrap();
}
Transform {
color_matrix,
from_corner: (rect_xs.0, rect_ys.0),
from_extents: (rect_xs.1 - rect_xs.0, rect_ys.1 - rect_ys.0),
to_corner,
to_extents,
}
}
}

13
src/transform.rs

@ -1,9 +1,12 @@
use image::{Rgb, GenericImage};
use rand::Rng;
const NCOLORS: usize = 3;
pub const NCOLORS: usize = 3;
#[derive(Debug, Clone)]
pub struct Transform {
pub color_matrix: [f32; NCOLORS * NCOLORS],
// TODO: make these f32s
pub from_corner: (u32, u32),
pub from_extents: (u32, u32),
pub to_corner: (f32, f32),
@ -16,8 +19,8 @@ impl Transform {
let x = x_off as f32 / self.from_extents.0 as f32;
for y_off in 0..self.from_extents.1 {
let y = y_off as f32 / self.from_extents.1 as f32;
let x_in = self.to_corner.0 + x * self.to_extents[0].0 + y * self.to_extents[0].1;
let y_in = self.to_corner.1 + x * self.to_extents[1].0 + y * self.to_extents[1].1;
let x_in = (self.to_corner.0 + x * self.to_extents[0].0 + y * self.to_extents[0].1) * dims.0 as f32;
let y_in = (self.to_corner.1 + x * self.to_extents[1].0 + y * self.to_extents[1].1) * dims.1 as f32;
let in_coords = (x_in, y_in);
// Get input color from pixels near sample
@ -32,14 +35,12 @@ 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());
println!("xy= ({} {}) frac=({} {}) area={}", x, y, frac.0, frac.1, area);
total_area += area;
for i in 0..NCOLORS {
in_pix[i] += val[i] * area;
}
}
}
println!("* xy_off=({} {}) in_coords=({} {}) in_coords_i32=({} {}) total_area={}", x_off, y_off, in_coords.0, in_coords.1, in_coords_i32.0, in_coords_i32.1, total_area);
// Compute the transformed color
let mut color = [0.0; NCOLORS];
@ -60,4 +61,6 @@ impl Transform {
}
}
}
}
Loading…
Cancel
Save