use anyhow::Result;
use defvar::defvar;
use opencv::core::{mean, multiply, absdiff, no_array, Mat, Vec2f, Vec3b};
use opencv::imgcodecs::{imread, imwrite};
use opencv::prelude::*;
use libdebayer::{DebayerInputImage, DebayerAlgorithm};
use std::fs::DirEntry;
use std::io;
use std::path::Path;
defvar! { KODAK_FOLDER_PATH: String = "../../benchmark/kodak".to_string() }
fn convert_to_rggb(bgr_image: &Mat) -> Result<Mat, opencv::Error> {
let mut bayer = Mat::new_rows_cols_with_default(
bgr_image.rows(),
bgr_image.cols(),
opencv::core::CV_8UC1,
opencv::core::Scalar::all(0.0)
)?;
for y in 0..bgr_image.rows() {
for x in 0..bgr_image.cols() {
let pixel = bgr_image.at_2d::<Vec3b>(y, x)?;
let value = {
if y % 2 == 0 {
if x % 2 == 0 {
pixel[2] } else {
pixel[1] }
} else {
if x % 2 == 0 {
pixel[1] } else {
pixel[0] }
}
};
*bayer.at_2d_mut::<u8>(y, x)? = value;
}
}
Ok(bayer)
}
fn convert_to_bggr(bgr_image: &Mat) -> Result<Mat, opencv::Error> {
let mut bayer = Mat::new_rows_cols_with_default(
bgr_image.rows(),
bgr_image.cols(),
opencv::core::CV_8UC1,
opencv::core::Scalar::all(0.0)
)?;
for y in 0..bgr_image.rows() {
for x in 0..bgr_image.cols() {
let pixel = bgr_image.at_2d::<Vec3b>(y, x)?;
let value = if y % 2 == 0 {
if x % 2 == 0 {
pixel[0] } else {
pixel[1] }
} else {
if x % 2 == 0 {
pixel[1] } else {
pixel[2] }
};
*bayer.at_2d_mut::<u8>(y, x)? = value;
}
}
Ok(bayer)
}
fn calculate_psnr(original: &Mat, processed: &Mat) -> Result<f64> {
let mut diff = Mat::default();
absdiff(original, processed, &mut diff)?;
let mut float_diff = Mat::default();
diff.convert_to(&mut float_diff, opencv::core::CV_32F, 1.0, 0.0)?;
multiply(&float_diff, &float_diff, &mut diff, 1.0, -1)?;
let mse = mean(&diff, &no_array())?[0];
if mse <= 1e-10 {
return Ok(100.0); }
let max_pixel_value = 255.0;
let psnr = 20.0 * (max_pixel_value / mse.sqrt()).log10();
Ok(psnr)
}
fn calculate_psnr_rb_at_br(original: &Mat, processed: &Mat) -> Result<f64> {
assert_eq!(original.channels(), 3);
assert_eq!(processed.channels(), 3);
assert_eq!(original.rows(), processed.rows());
assert_eq!(original.cols(), processed.cols());
assert_eq!(original.rows() % 2, 0);
assert_eq!(original.cols() % 2, 0);
let mut diff = Mat::new_rows_cols_with_default(
original.rows() / 2,
original.cols() / 2,
opencv::core::CV_32FC2,
opencv::core::Scalar::default(),
)?;
let mut sum_squared_diff = 0.0;
let mut count = 0;
for y in (4..original.rows() - 4).step_by(2) {
for x in (4..original.cols() - 4).step_by(2) {
let orig_tl = original.at_2d::<Vec3b>(y, x)?;
let proc_tl = processed.at_2d::<Vec3b>(y, x)?;
let orig_br = original.at_2d::<Vec3b>(y + 1, x + 1)?;
let proc_br = processed.at_2d::<Vec3b>(y + 1, x + 1)?;
let diff_b = orig_tl[1] as f64 - proc_tl[1] as f64;
let diff_r = orig_br[1] as f64 - proc_br[1] as f64;
sum_squared_diff += diff_r * diff_r + diff_b * diff_b;
count += 2;
*diff.at_2d_mut::<Vec2f>(y / 2, x / 2)? = Vec2f::from([diff_r as f32, diff_b as f32]);
}
}
let mse = sum_squared_diff / count as f64;
println!("MSE: {}", mse);
println!("Count: {}", count);
println!("Sum of squared differences: {}", sum_squared_diff);
if mse <= 1e-10 {
println!("Warning: Images appear to be identical or very close.");
Ok(100.0) } else {
let max_pixel_value = 255.0;
let psnr = 20.0 * (max_pixel_value / mse.sqrt()).log10();
Ok(psnr)
}
}
fn main() -> Result<(), anyhow::Error> {
let kodak_path = Path::new((*KODAK_FOLDER_PATH).as_str());
let test_files: Vec<Result<DirEntry, io::Error>> = kodak_path.read_dir()?.filter(|e| {
if let Ok(e) = e {
let fname = e.file_name().into_string().unwrap();
fname.contains("kodim") && fname.contains(".png") && (!fname.ends_with(".out.png"))
} else {
false
}
}).collect();
let mut img_count = 0;
let mut psnr_sum = 0.0f64;
let mut g_psnr_sum = 0.0f64;
for t in test_files {
if let Ok(t) = t {
let fname = t.path().into_os_string().into_string().unwrap();
println!("Processing: {fname}");
let img = imread(fname.as_str(), opencv::imgcodecs::IMREAD_COLOR)?;
let bayer_image = convert_to_rggb(&img)?;
let mut debayer_input_image = DebayerInputImage::try_from(&bayer_image)?;
let debayer_output_image = debayer_input_image.debayer(DebayerAlgorithm::Menon2007Rggb2Bgr)?;
let debayer_output_mat = Mat::try_from(debayer_output_image)?;
let psnr = calculate_psnr(&img, &debayer_output_mat)?;
let g_psnr = calculate_psnr_rb_at_br(&img, &debayer_output_mat)?;
println!("psnr: {psnr}");
println!("g_psnr: {g_psnr}");
psnr_sum += psnr;
g_psnr_sum += g_psnr;
img_count += 1;
}
}
println!("Average PSNR: {} dB", psnr_sum/(img_count as f64));
println!("Average G-Channel PSNR: {} dB", g_psnr_sum/(img_count as f64));
Ok(())
}