#![no_std]
#![warn(missing_docs)]
extern crate alloc;
mod cfa;
mod error;
mod bayer;
mod xtrans;
mod lab;
use core::fmt;
pub use cfa::{CfaPattern, Channel};
pub use error::DemosaicError;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Algorithm {
Bilinear,
Mhc,
Ppg,
Ahd,
Markesteijn1,
Markesteijn3,
Dht,
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bilinear => f.write_str("Bilinear"),
Self::Mhc => f.write_str("MHC"),
Self::Ppg => f.write_str("PPG"),
Self::Ahd => f.write_str("AHD"),
Self::Markesteijn1 => f.write_str("Markesteijn (1-pass)"),
Self::Markesteijn3 => f.write_str("Markesteijn (3-pass)"),
Self::Dht => f.write_str("DHT"),
}
}
}
pub fn demosaic(
input: &[f32],
width: usize,
height: usize,
cfa: &CfaPattern,
algorithm: Algorithm,
output: &mut [f32],
) -> Result<(), DemosaicError> {
let npix = width * height;
if input.len() != npix {
return Err(DemosaicError::InputSizeMismatch { expected: npix, got: input.len() });
}
if output.len() != 3 * npix {
return Err(DemosaicError::OutputSizeMismatch { expected: 3 * npix, got: output.len() });
}
if cfa.is_bayer() {
match algorithm {
Algorithm::Bilinear => bayer::bilinear(input, width, height, cfa, output),
Algorithm::Mhc => bayer::mhc(input, width, height, cfa, output),
Algorithm::Ppg => bayer::ppg(input, width, height, cfa, output),
Algorithm::Ahd => bayer::ahd(input, width, height, cfa, output),
_ => {
return Err(DemosaicError::UnsupportedAlgorithm {
algorithm: match algorithm {
Algorithm::Markesteijn1 => "Markesteijn1",
Algorithm::Markesteijn3 => "Markesteijn3",
Algorithm::Dht => "DHT",
_ => unreachable!(),
},
cfa: "Bayer",
});
}
}
} else {
match algorithm {
Algorithm::Bilinear => xtrans::bilinear(input, width, height, cfa, output),
Algorithm::Markesteijn1 | Algorithm::Markesteijn3 => {
if width < 64 || height < 64 {
return Err(DemosaicError::ImageTooSmall {
min_width: 64,
min_height: 64,
});
}
if algorithm == Algorithm::Markesteijn3 {
xtrans::markesteijn3(input, width, height, cfa, output);
} else {
xtrans::markesteijn1(input, width, height, cfa, output);
}
}
Algorithm::Dht => {
xtrans::dht(input, width, height, cfa, output);
}
_ => {
return Err(DemosaicError::UnsupportedAlgorithm {
algorithm: match algorithm {
Algorithm::Mhc => "MHC",
Algorithm::Ppg => "PPG",
Algorithm::Ahd => "AHD",
_ => unreachable!(),
},
cfa: "X-Trans",
});
}
}
}
Ok(())
}
pub fn demosaic_interleaved(
input: &[f32],
width: usize,
height: usize,
cfa: &CfaPattern,
algorithm: Algorithm,
output: &mut [f32],
) -> Result<(), DemosaicError> {
let npix = width * height;
let mut planar = alloc::vec![0.0f32; 3 * npix];
demosaic(input, width, height, cfa, algorithm, &mut planar)?;
planar_to_interleaved(&planar, output);
Ok(())
}
pub fn planar_to_interleaved(planar: &[f32], interleaved: &mut [f32]) {
let len = planar.len();
assert_eq!(len, interleaved.len());
assert_eq!(len % 3, 0);
let npix = len / 3;
for i in 0..npix {
interleaved[3 * i] = planar[i];
interleaved[3 * i + 1] = planar[npix + i];
interleaved[3 * i + 2] = planar[2 * npix + i];
}
}
pub fn interleaved_to_planar(interleaved: &[f32], planar: &mut [f32]) {
let len = interleaved.len();
assert_eq!(len, planar.len());
assert_eq!(len % 3, 0);
let npix = len / 3;
for i in 0..npix {
planar[i] = interleaved[3 * i];
planar[npix + i] = interleaved[3 * i + 1];
planar[2 * npix + i] = interleaved[3 * i + 2];
}
}