use crate::*;
use edge_detection::canny;
use image::{imageops::Lanczos3, DynamicImage, Luma};
use std::{
io::{stdout, Write},
time::Instant,
};
#[derive(StructOpt, Debug)]
pub struct Param {
#[structopt(parse(from_os_str))]
image_dir_or_file: PathBuf,
#[structopt(parse(from_os_str))]
output_dir_or_file: PathBuf,
#[structopt(short = "s", long, default_value = "2.35")]
sigma: f32,
#[structopt(short = "S", long = "strong", default_value = "0.18")]
thr_strong: f32,
#[structopt(short = "w", long = "weak", default_value = "0.08")]
thr_weak: f32,
#[structopt(long, parse(try_from_str = opt_crop))]
crop: Option<(u32, u32, u32, u32)>,
#[structopt(long, parse(try_from_str = opt_resize))]
resize: Option<(u32, u32)>,
#[structopt(short, long)]
zoom: Option<f32>,
#[structopt(long = "skip", default_value = "0")]
i_skip: usize,
#[structopt(long = "step", default_value = "1")]
i_step: usize,
#[structopt(long = "ctr", default_value = "1")]
i_ctr: u32,
#[structopt(short, long, parse(from_occurrences))]
verbose: u8,
}
pub fn main(
Param {
image_dir_or_file,
output_dir_or_file,
sigma,
thr_weak,
thr_strong,
crop,
resize,
zoom,
i_skip,
i_step,
i_ctr,
verbose,
}: Param,
) {
let verbose = verbose > 0;
let srcs: Box<dyn Iterator<Item = Result<PathBuf, String>>>;
let dsts: Box<dyn Iterator<Item = PathBuf>>;
if image_dir_or_file.is_file() {
if output_dir_or_file.exists() && !output_dir_or_file.is_file() {
panic!(
"\"{}\" already existed but not suitable as output file",
output_dir_or_file.to_string_lossy()
)
}
srcs = Box::new(vec![Ok(image_dir_or_file)].into_iter());
dsts = Box::new(vec![output_dir_or_file].into_iter());
} else if image_dir_or_file.is_dir() {
if output_dir_or_file.exists() && !output_dir_or_file.is_dir() {
panic!(
"\"{}\" already existed but not suitable as output dir",
output_dir_or_file.to_string_lossy()
)
}
util::create_dir(&output_dir_or_file);
srcs = Box::new(
util::whether_dir(image_dir_or_file, "images", "image", verbose)
.skip(i_skip)
.step_by(i_step),
);
dsts = Box::new(
(i_ctr..=u32::MAX)
.into_iter()
.map(|n| output_dir_or_file.join(format!("{:06}.png", n))),
);
} else {
panic!(
"Invalid image(s) path \"{}\"",
image_dir_or_file.to_string_lossy()
);
}
let mut now = Instant::now();
for (ctr, (src, dst)) in srcs.zip(dsts).enumerate() {
if verbose {
print!("[{:06}] ", ctr);
}
#[rustfmt::skip]
let mut img = util::img3(
match src {
Ok(p) => {
if verbose {
print!("\"{}\" ", p.file_name().unwrap().to_string_lossy());
}
match image::open(&p) {
Ok(i) => DynamicImage::ImageLuma8(i.to_luma8()),
Err(e) => { match verbose {
true => println!("Failed to open: {:?}", e),
false => print!("F"),
} continue },
}
},
Err(e) => { match verbose {
true => println!("Failed to access: {}", e),
false => print!("E"),
} continue },
},
crop,
resize,
zoom,
Lanczos3,
).to_luma8();
img = canny(img, sigma, thr_strong, thr_weak)
.as_image()
.to_luma8();
img.pixels_mut().for_each(|Luma([n])| {
if *n != 0 {
*n = 255;
}
});
match img.save(&dst) {
Ok(_) => match verbose {
true => {
println!("{:05.3} secs", now.elapsed().as_secs_f32());
now = Instant::now();
}
false => {
if ctr % 50 == 0 {
print!("[{}]", ctr);
} else {
print!(".");
}
}
},
Err(e) => match verbose {
true => println!("Failed to save to \"{}\": {:?}", dst.to_string_lossy(), e),
false => print!("S"),
},
}
stdout().flush().ok();
}
}