use image::{GenericImageView, Pixel, RgbaImage};
use scirs2_vision::transform::{estimate_affine_transform, warp_affine, AffineTransform};
use std::env;
use std::path::Path;
#[allow(dead_code)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <image_path>", args[0]);
std::process::exit(1);
}
let img_path = Path::new(&args[1]);
let img = image::open(img_path)?;
println!("Loaded image: {:?}", img.dimensions());
std::fs::create_dir_all("output")?;
println!("\nApplying translation...");
let translation = AffineTransform::translation(50.0, 30.0);
let translated = warp_affine(
&img,
&translation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
translated.save("output/translated.png")?;
println!(" Saved to output/translated.png");
println!("\nApplying scaling...");
let scaling = AffineTransform::scaling(1.5, 0.7);
let scaled = warp_affine(
&img,
&scaling,
None,
None,
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
scaled.save("output/scaled.png")?;
println!(" Saved to output/scaled.png");
println!("\nApplying rotation...");
let angle = std::f64::consts::PI / 6.0; let rotation = AffineTransform::rotation(angle);
let (width, height) = img.dimensions();
let rotated = warp_affine(
&img,
&rotation,
Some((1.5 * width as f64) as u32),
Some((1.5 * height as f64) as u32),
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
rotated.save("output/rotated.png")?;
println!(" Saved to output/rotated.png");
println!("\nApplying combined transformation...");
let scale = AffineTransform::scaling(1.2, 0.8);
let rotate = AffineTransform::rotation(std::f64::consts::PI / 4.0); let translate = AffineTransform::translation(100.0, 50.0);
let combined = translate.compose(&rotate.compose(&scale));
let transformed = warp_affine(
&img,
&combined,
Some((2.0 * width as f64) as u32),
Some((2.0 * height as f64) as u32),
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
transformed.save("output/combined.png")?;
println!(" Saved to output/combined.png");
println!("\nDemonstrating border modes...");
let small_rotation = AffineTransform::rotation(std::f64::consts::PI / 12.0);
let transparent = warp_affine(
&img,
&small_rotation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
transparent.save("output/border_transparent.png")?;
let replicate = warp_affine(
&img,
&small_rotation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Replicate,
)?;
replicate.save("output/border_replicate.png")?;
let reflect = warp_affine(
&img,
&small_rotation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Reflect,
)?;
reflect.save("output/border_reflect.png")?;
let wrap = warp_affine(
&img,
&small_rotation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Wrap,
)?;
wrap.save("output/border_wrap.png")?;
let constant = warp_affine(
&img,
&small_rotation,
None,
None,
scirs2_vision::transform::affine::BorderMode::Constant(image::Rgba([255, 0, 0, 255])),
)?;
constant.save("output/border_constant.png")?;
println!(" Saved all border mode examples to output/border_*.png");
println!("\nEstimating affine transform from points...");
let src_points = vec![
(100.0, 100.0), (400.0, 100.0), (400.0, 300.0), (100.0, 300.0), ];
let dst_points = vec![
(120.0, 80.0), (450.0, 120.0), (420.0, 350.0), (80.0, 330.0), ];
let estimated_transform = estimate_affine_transform(&src_points, &dst_points)?;
let (width, height) = img.dimensions();
let mut visualization = RgbaImage::new(width, height);
for y in 0..height {
for x in 0..width {
let mut pixel = img.get_pixel(x, y).to_rgba();
pixel[3] = 128; visualization.put_pixel(x, y, pixel);
}
}
draw_quadrilateral(&mut visualization, &src_points, [0, 0, 255, 255]);
draw_quadrilateral(&mut visualization, &dst_points, [255, 0, 0, 255]);
for x in 100..=400 {
for y in 100..=300 {
let (tx, ty) = estimated_transform.transform_point(x as f64, y as f64);
if tx >= 0.0 && tx < width as f64 && ty >= 0.0 && ty < height as f64 {
visualization.put_pixel(tx as u32, ty as u32, image::Rgba([255, 255, 0, 255]));
}
}
}
visualization.save("output/estimated_transform.png")?;
println!(" Saved visualization to output/estimated_transform.png");
let warped = warp_affine(
&img,
&estimated_transform,
None,
None,
scirs2_vision::transform::affine::BorderMode::Transparent,
)?;
warped.save("output/warped_estimated.png")?;
println!(" Saved warped image to output/warped_estimated.png");
println!("\nAffine transformation example completed successfully!");
Ok(())
}
#[allow(dead_code)]
fn draw_quadrilateral(img: &mut RgbaImage, points: &[(f64, f64)], color: [u8; 4]) {
if points.len() != 4 {
return;
}
let color = image::Rgba(color);
draw_line(img, points[0], points[1], color);
draw_line(img, points[1], points[2], color);
draw_line(img, points[2], points[3], color);
draw_line(img, points[3], points[0], color);
for &(x, y) in points {
draw_circle(img, x as u32, y as u32, 5, color);
}
}
#[allow(dead_code)]
fn draw_line(img: &mut RgbaImage, p1: (f64, f64), p2: (f64, f64), color: image::Rgba<u8>) {
let (width, height) = img.dimensions();
let (x0, y0) = (p1.0 as i32, p1.1 as i32);
let (x1, y1) = (p2.0 as i32, p2.1 as i32);
let dx = (x1 - x0).abs();
let dy = (y1 - y0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx - dy;
let mut x = x0;
let mut y = y0;
while x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
img.put_pixel(x as u32, y as u32, color);
if x == x1 && y == y1 {
break;
}
let e2 = 2 * err;
if e2 > -dy {
err -= dy;
x += sx;
}
if e2 < dx {
err += dx;
y += sy;
}
}
}
#[allow(dead_code)]
fn draw_circle(img: &mut RgbaImage, cx: u32, cy: u32, radius: u32, color: image::Rgba<u8>) {
let (width, height) = img.dimensions();
for y in (cy.saturating_sub(radius))..=(cy + radius).min(height - 1) {
for x in (cx.saturating_sub(radius))..=(cx + radius).min(width - 1) {
let dx = x as i32 - cx as i32;
let dy = y as i32 - cy as i32;
if dx * dx + dy * dy <= radius as i32 * radius as i32 {
img.put_pixel(x, y, color);
}
}
}
}