use clap::{Parser, ValueEnum};
use retinex::{
extract_illumination, multi_scale_retinex, multi_scale_retinex_color_restored,
multi_scale_retinex_full, normalize_reflectance, single_scale_retinex,
single_scale_retinex_color_restored, single_scale_retinex_full,
};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(name = "retinex")]
#[command(about = "Apply Retinex enhancement to images", long_about = None)]
struct Cli {
#[arg(help = "Input image file path")]
input: PathBuf,
#[arg(help = "Output image file path (reflectance result)")]
output: PathBuf,
#[arg(short, long, value_enum, default_value = "single")]
mode: Mode,
#[arg(long, value_delimiter = ',', default_value = "15.0")]
sigmas: Vec<f32>,
#[arg(long)]
illumination: Option<PathBuf>,
#[arg(long)]
reflectance: Option<PathBuf>,
#[arg(long, default_value_t = false)]
color_restore: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
enum Mode {
Single,
Multi,
}
fn main() {
let cli = Cli::parse();
if cli.sigmas.is_empty() {
eprintln!("Sigmas cannot be empty");
std::process::exit(1);
}
if let Some(&invalid) = cli.sigmas.iter().find(|&&sigma| sigma <= 0.0) {
eprintln!("Sigmas must be positive, got {invalid}");
std::process::exit(1);
}
let image = match image::open(&cli.input) {
Ok(img) => img,
Err(err) => {
eprintln!("Failed to open {}: {err}", cli.input.display());
std::process::exit(1);
}
};
if let Some(illum_path) = &cli.illumination {
let illum_result = match cli.mode {
Mode::Single => extract_illumination(&image, cli.sigmas[0]),
Mode::Multi => {
extract_illumination(&image, cli.sigmas[0])
}
};
match illum_result {
Ok(illum) => {
if let Err(err) = illum.save(illum_path) {
eprintln!(
"Failed to save illumination {}: {err}",
illum_path.display()
);
std::process::exit(1);
}
println!("Saved illumination to {}", illum_path.display());
}
Err(err) => {
eprintln!("Failed to extract illumination: {err}");
std::process::exit(1);
}
}
}
if let Some(refl_path) = &cli.reflectance {
let refl_result = match cli.mode {
Mode::Single => {
let output = single_scale_retinex_full(&image, cli.sigmas[0]);
output.map(|o| normalize_reflectance(&o.reflectance))
}
Mode::Multi => {
let output = multi_scale_retinex_full(&image, &cli.sigmas);
output.map(|o| normalize_reflectance(&o.reflectance))
}
};
match refl_result {
Ok(refl) => {
if let Err(err) = refl.save(refl_path) {
eprintln!("Failed to save reflectance {}: {err}", refl_path.display());
std::process::exit(1);
}
println!("Saved raw reflectance to {}", refl_path.display());
}
Err(err) => {
eprintln!("Failed to extract reflectance: {err}");
std::process::exit(1);
}
}
}
let result = if cli.color_restore {
match cli.mode {
Mode::Single => single_scale_retinex_color_restored(&image, cli.sigmas[0]),
Mode::Multi => multi_scale_retinex_color_restored(&image, &cli.sigmas),
}
} else {
match cli.mode {
Mode::Single => single_scale_retinex(&image, cli.sigmas[0]),
Mode::Multi => multi_scale_retinex(&image, &cli.sigmas),
}
};
match result {
Ok(processed) => {
if let Err(err) = processed.save(&cli.output) {
eprintln!("Failed to save {}: {err}", cli.output.display());
std::process::exit(1);
}
println!("Saved result to {}", cli.output.display());
}
Err(err) => {
eprintln!("Processing failed: {err}");
std::process::exit(1);
}
}
}