Browse Source

initial commit -- need to seriously revise transform algorithm

main
Thomas Johnson 10 months ago
commit
1bdd4e3eda
  1. 5
      .gitignore
  2. 7
      Cargo.toml
  3. 28
      src/main.rs
  4. 91
      src/transform.rs

5
.gitignore

@ -0,0 +1,5 @@
/target
Cargo.lock
tests/
out/
*.swp

7
Cargo.toml

@ -0,0 +1,7 @@
[package]
name = "fractal_compression"
version = "0.1.0"
edition = "2018"
[dependencies]
image = "0.23"

28
src/main.rs

@ -0,0 +1,28 @@
use image::{ImageBuffer, Rgb};
mod 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 dims = image_in_u16.dimensions();
let image_in: ImageBuffer<Rgb<f64>, Vec<f64>> = ImageBuffer::from_fn(dims.0, dims.1, |x, y| {
let pix = image_in_u16.get_pixel(x, y);
Rgb([pix[0] as f64 / 65535.0, pix[1] as f64 / 65535.0, pix[2] as f64 / 65535.0])
});
let mut image_out: ImageBuffer<Rgb<f64>, Vec<f64>> = 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: [(0.0, 0.0), ((dims.0 / 2) as f64, 0.0), (0.0, (dims.1 - 1) as f64)],
to: [(0.0, 0.0), ((dims.0 - 1) as f64, (dims.1 / 2) as f64), (0.0, (dims.1 - 1) as f64)],
};
transform.apply_add(&image_in, &mut image_out, dims);
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");
}

91
src/transform.rs

@ -0,0 +1,91 @@
use image::{ImageBuffer, Rgb, GenericImage};
const NCOLORS: usize = 3;
const OVERSAMPLE_RATE: usize = 4;
pub struct Transform {
pub color_matrix: [f64; NCOLORS * NCOLORS],
pub from: [(f64, f64); 3],
pub to: [(f64, f64); 3],
}
impl Transform {
pub fn apply_add<I: GenericImage<Pixel=Rgb<f64>>, O: GenericImage<Pixel=Rgb<f64>>>(&self, input: &I, output: &mut O, dims: (u32, u32)) {
let v1 = (self.from[0].0 - self.from[1].0, self.from[0].1 - self.from[1].1);
let v2 = (self.from[2].0 - self.from[1].0, self.from[2].1 - self.from[1].1);
let v3 = (self.from[0].0 - self.from[2].0, self.from[0].1 - self.from[2].1);
let l1sqf = v1.0 * v1.0 + v1.1 * v1.1;
let l2sqf = v2.0 * v2.0 + v2.1 * v2.1;
let l3sqf = v3.0 * v3.0 + v3.1 * v3.1;
let v1 = (self.to[0].0 - self.to[1].0, self.to[0].1 - self.to[1].1);
let v2 = (self.to[2].0 - self.to[1].0, self.to[2].1 - self.to[1].1);
let v3 = (self.to[0].0 - self.to[2].0, self.to[0].1 - self.to[2].1);
let l1sqt = v1.0 * v1.0 + v1.1 * v1.1;
let l2sqt = v2.0 * v2.0 + v2.1 * v2.1;
let l3sqt = v3.0 * v3.0 + v3.1 * v3.1;
let lmaxsq = [l1sqf, l2sqf, l3sqf, l1sqt, l2sqt, l3sqt].iter().cloned().reduce(|x, y| if x > y { x } else { y }).unwrap();
let sample_rate = lmaxsq.sqrt().ceil() as usize * OVERSAMPLE_RATE;
let twice_area_from = self.from[0].0 * self.from[1].1 + self.from[1].0 * self.from[2].1 + self.from[2].0 * self.from[0].1 - (self.from[0].1 * self.from[1].0 + self.from[1].1 * self.from[2].0 + self.from[2].1 * self.from[0].0);
let twice_area_to = self.to[0].0 * self.to[1].1 + self.to[1].0 * self.to[2].1 + self.to[2].0 * self.to[0].1 - (self.to[0].1 * self.to[1].0 + self.to[1].1 * self.to[2].0 + self.to[2].1 * self.to[0].0);
let scale_ratio = twice_area_to / twice_area_from / (OVERSAMPLE_RATE * OVERSAMPLE_RATE) as f64 * 0.5;
// Sample at barycentric coordinates
for α_idx in 0..=sample_rate {
for β_idx in 0..=(sample_rate - α_idx) {
let α = α_idx as f64 * (sample_rate as f64).recip();
let β = β_idx as f64 * (sample_rate as f64).recip();
let γ = 1.0 - α - β;
// Find absolute coordinates from barycentric
let in_coords = (α * self.from[0].0 + β * self.from[1].0 + γ * self.from[2].0, α * self.from[0].1 + β * self.from[1].1 + γ * self.from[2].1);
let out_coords = (α * self.to[0].0 + β * self.to[1].0 + γ * self.to[2].0, α * self.to[0].1 + β * self.to[1].1 + γ * self.to[2].1);
// let out_coords = (2.0 * out_coords.0, 2.0 * out_coords.1);
// Get input color from pixels near sample
let mut in_pix = [0.0; NCOLORS];
let mut total_area: f64 = 0.0;
let in_coords_u32 = (in_coords.0.floor() as u32, in_coords.1.floor() as u32);
let out_coords_u32 = (out_coords.0.floor() as u32, out_coords.1.floor() as u32);
for x in in_coords_u32.0..=in_coords_u32.0 + 1 {
for y in in_coords_u32.1..=in_coords_u32.1 + 1 {
if x >= dims.0 || y >= dims.1 {
continue;
}
let val = input.get_pixel(x, y).0;
let area = ((x as f64 - in_coords.0) * (y as f64 - in_coords.1)).abs();
for i in 0..NCOLORS {
in_pix[i] += val[i] * area;
}
total_area += area;
}
}
for i in 0..NCOLORS {
in_pix[i] *= total_area.recip();
}
// Compute the transformed color
let mut color = [0.0; NCOLORS];
for i in 0..NCOLORS {
for j in 0..NCOLORS {
color[i] += self.color_matrix[i * NCOLORS + j] * in_pix[j] * scale_ratio;
}
}
// Write output color to nearby pixels
for x in out_coords_u32.0..=out_coords_u32.0 + 1 {
for y in out_coords_u32.1..=out_coords_u32.1 + 1 {
if x >= dims.0 || y >= dims.1 {
continue;
}
let mut pix = output.get_pixel(x, y).0;
let area = ((x as f64 - out_coords.0) * (y as f64 - out_coords.1)).abs();
for i in 0..NCOLORS {
pix[i] += color[i] * area;
}
output.put_pixel(x, y, Rgb(pix));
}
}
}
}
}
}
Loading…
Cancel
Save