use crate::core::image::{RawImage, RgbImage};
#[derive(Debug, Clone)]
pub enum DemosaicError {
BufferSizeMismatch {
expected: usize,
actual: usize,
},
InvalidDimensions,
UnsupportedAlgorithm(&'static str),
}
impl std::fmt::Display for DemosaicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DemosaicError::BufferSizeMismatch { expected, actual } => {
write!(
f,
"buffer size mismatch: expected {} elements, got {}",
expected, actual
)
}
DemosaicError::InvalidDimensions => write!(f, "invalid image dimensions"),
DemosaicError::UnsupportedAlgorithm(name) => {
write!(f, "demosaicing algorithm '{}' is not yet implemented", name)
}
}
}
}
impl std::error::Error for DemosaicError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DemosaicMethod {
#[default]
Auto,
Bayer(BayerAlgorithm),
XTrans(XTransAlgorithm),
None,
}
impl std::fmt::Display for DemosaicMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DemosaicMethod::Auto => write!(f, "Auto"),
DemosaicMethod::Bayer(algo) => write!(f, "Bayer({})", algo),
DemosaicMethod::XTrans(algo) => write!(f, "XTrans({})", algo),
DemosaicMethod::None => write!(f, "None"),
}
}
}
impl DemosaicMethod {
pub fn to_demosaic(&self) -> Box<dyn Demosaic + Send + Sync> {
match self {
DemosaicMethod::Auto => {
Box::new(bayer::Amaze)
}
DemosaicMethod::Bayer(algo) => algo.to_demosaic(),
DemosaicMethod::XTrans(algo) => algo.to_demosaic(),
DemosaicMethod::None => Box::new(NoDemosaic),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BayerAlgorithm {
#[default]
Amaze,
Lmmse,
Rcd,
Bilinear,
}
impl std::fmt::Display for BayerAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BayerAlgorithm::Amaze => write!(f, "AMaZE"),
BayerAlgorithm::Lmmse => write!(f, "LMMSE"),
BayerAlgorithm::Rcd => write!(f, "RCD"),
BayerAlgorithm::Bilinear => write!(f, "Bilinear"),
}
}
}
impl BayerAlgorithm {
pub fn to_demosaic(&self) -> Box<dyn Demosaic + Send + Sync> {
match self {
BayerAlgorithm::Amaze => Box::new(bayer::Amaze),
BayerAlgorithm::Lmmse => Box::new(bayer::Lmmse),
BayerAlgorithm::Rcd => Box::new(bayer::Rcd),
BayerAlgorithm::Bilinear => Box::new(Bilinear),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum XTransAlgorithm {
#[default]
Markesteijn,
Markesteijn3Pass,
Fast,
}
impl std::fmt::Display for XTransAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
XTransAlgorithm::Markesteijn => write!(f, "Markesteijn"),
XTransAlgorithm::Markesteijn3Pass => write!(f, "Markesteijn 3-Pass"),
XTransAlgorithm::Fast => write!(f, "Fast"),
}
}
}
impl XTransAlgorithm {
pub fn to_demosaic(&self) -> Box<dyn Demosaic + Send + Sync> {
match self {
XTransAlgorithm::Markesteijn => Box::new(xtrans::Markesteijn),
XTransAlgorithm::Markesteijn3Pass => Box::new(xtrans::Markesteijn3Pass),
XTransAlgorithm::Fast => Box::new(xtrans::XTransFast),
}
}
}
pub trait Demosaic {
fn demosaic_into(&self, raw: &RawImage, output: &mut [u16]) -> Result<(), DemosaicError>;
#[must_use]
fn demosaic(&self, raw: &RawImage) -> RgbImage {
let width = raw.active_area().size.width;
let height = raw.active_area().size.height;
let mut data = vec![0u16; (width as usize) * (height as usize) * 3];
self.demosaic_into(raw, &mut data)
.expect("demosaic_into failed with correctly sized buffer");
RgbImage::new(width, height, data)
}
}
mod bilinear;
pub use bilinear::Bilinear;
pub mod bayer;
pub mod xtrans;
pub struct NoDemosaic;
impl Demosaic for NoDemosaic {
fn demosaic_into(&self, raw: &RawImage, output: &mut [u16]) -> Result<(), DemosaicError> {
let width = raw.active_area().size.width as usize;
let height = raw.active_area().size.height as usize;
let x_offset = raw.active_area().origin.x as usize;
let y_offset = raw.active_area().origin.y as usize;
let raw_width = raw.width() as usize;
let expected_size = width * height * 3;
if output.len() != expected_size {
return Err(DemosaicError::BufferSizeMismatch {
expected: expected_size,
actual: output.len(),
});
}
for y in 0..height {
for x in 0..width {
let raw_idx = (y + y_offset) * raw_width + (x + x_offset);
let out_idx = (y * width + x) * 3;
let value = raw.data[raw_idx];
output[out_idx] = value;
output[out_idx + 1] = value;
output[out_idx + 2] = value;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_demosaic_error_display() {
let err = DemosaicError::BufferSizeMismatch {
expected: 300,
actual: 100,
};
let msg = format!("{}", err);
assert!(msg.contains("300"));
assert!(msg.contains("100"));
let err = DemosaicError::InvalidDimensions;
let msg = format!("{}", err);
assert!(msg.contains("dimension"));
}
#[test]
fn test_demosaic_method_default() {
let method = DemosaicMethod::default();
assert_eq!(method, DemosaicMethod::Auto);
}
#[test]
fn test_bayer_algorithm_default() {
let algo = BayerAlgorithm::default();
assert_eq!(algo, BayerAlgorithm::Amaze);
}
#[test]
fn test_xtrans_algorithm_default() {
let algo = XTransAlgorithm::default();
assert_eq!(algo, XTransAlgorithm::Markesteijn);
}
#[test]
fn test_demosaic_method_variants() {
let _ = DemosaicMethod::Auto;
let _ = DemosaicMethod::Bayer(BayerAlgorithm::Bilinear);
let _ = DemosaicMethod::XTrans(XTransAlgorithm::Fast);
let _ = DemosaicMethod::None;
}
}