Browse Source

revised transform operation

main
Thomas Johnson 12 months ago
parent
commit
cbf93f7a3e
  1. 8
      src/main.rs
  2. 87
      src/transform.rs

8
src/main.rs

@ -19,8 +19,12 @@ fn main() {
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: [(0.0, 0.0), ((dims.0 / 2) as f32, 0.0), (0.0, (dims.1 - 1) as f32)],
to: [(0.0, 0.0), ((dims.0 - 1) as f32, (dims.1 / 2) as f32), (0.0, (dims.1 - 1) as f32)],
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)],
};
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| {

87
src/transform.rs

@ -1,89 +1,62 @@
use image::{Rgb, GenericImage};
const NCOLORS: usize = 3;
const OVERSAMPLE_RATE: usize = 4;
pub struct Transform {
pub color_matrix: [f32; NCOLORS * NCOLORS],
pub from: [(f32, f32); 3],
pub to: [(f32, f32); 3],
pub from_corner: (u32, u32),
pub from_extents: (u32, u32),
pub to_corner: (f32, f32),
pub to_extents: [(f32, f32); 2],
}
impl Transform {
pub fn apply_add<I: GenericImage<Pixel=Rgb<f32>>, O: GenericImage<Pixel=Rgb<f32>>>(&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 f32 * 0.5;
// Sample at barycentric coordinates
for α_idx in 0..=sample_rate {
for β_idx in 0..=(sample_rate - α_idx) {
let α = α_idx as f32 * (sample_rate as f32).recip();
let β = β_idx as f32 * (sample_rate as f32).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);
for x_off in 0..self.from_extents.0 {
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 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 mut total_area: f32 = 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 {
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 {
for y in in_coords_i32.1..=in_coords_i32.1 + 1 {
if x < 0 || y < 0 || x >= dims.0 as i32 || y >= dims.1 as i32 {
continue;
}
let val = input.get_pixel(x, y).0;
let area = ((x as f32 - in_coords.0) * (y as f32 - in_coords.1)).abs();
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;
}
total_area += area;
}
}
for i in 0..NCOLORS {
in_pix[i] *= total_area.recip();
}
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];
for i in 0..NCOLORS {
for j in 0..NCOLORS {
color[i] += self.color_matrix[i * NCOLORS + j] * in_pix[j] * scale_ratio;
color[i] += self.color_matrix[i * NCOLORS + j] * in_pix[j];
}
}
// 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 f32 - out_coords.0) * (y as f32 - out_coords.1)).abs();
for i in 0..NCOLORS {
pix[i] += color[i] * area;
}
output.put_pixel(x, y, Rgb(pix));
}
// Put the resulting color into the output image
let x_out = self.from_corner.0 + x_off;
let y_out = self.from_corner.1 + y_off;
let mut pix = output.get_pixel(x_out, y_out).0;
for i in 0..NCOLORS {
pix[i] += color[i];
}
output.put_pixel(x_out, y_out, Rgb(pix));
}
}
}

Loading…
Cancel
Save