#![no_std]
#![warn(missing_docs)]
extern crate alloc;
mod cfa;
mod error;
mod bayer;
mod quad_bayer;
mod remosaic;
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 BayerAlgorithm {
Bilinear,
Mhc,
Ppg,
Ahd,
Vng,
}
impl fmt::Display for BayerAlgorithm {
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::Vng => f.write_str("VNG"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Algorithm {
Bilinear,
Mhc,
Ppg,
Ahd,
Vng,
Markesteijn1,
Markesteijn3,
Dht,
QuadPpg,
}
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::Vng => f.write_str("VNG"),
Self::Markesteijn1 => f.write_str("Markesteijn (1-pass)"),
Self::Markesteijn3 => f.write_str("Markesteijn (3-pass)"),
Self::Dht => f.write_str("DHT"),
Self::QuadPpg => f.write_str("Quad-PPG"),
}
}
}
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),
Algorithm::Vng => bayer::vng(input, width, height, cfa, output),
_ => {
return Err(DemosaicError::UnsupportedAlgorithm {
algorithm: match algorithm {
Algorithm::Markesteijn1 => "Markesteijn1",
Algorithm::Markesteijn3 => "Markesteijn3",
Algorithm::Dht => "DHT",
Algorithm::QuadPpg => "Quad-PPG",
_ => unreachable!(),
},
cfa: "Bayer",
});
}
}
} else if cfa.is_quad_bayer() {
match algorithm {
Algorithm::Bilinear => quad_bayer::bilinear(input, width, height, cfa, output),
Algorithm::QuadPpg => quad_bayer::qppg(input, width, height, cfa, output),
_ => {
return Err(DemosaicError::UnsupportedAlgorithm {
algorithm: match algorithm {
Algorithm::Mhc => "MHC",
Algorithm::Ppg => "PPG",
Algorithm::Ahd => "AHD",
Algorithm::Vng => "VNG",
Algorithm::Markesteijn1 => "Markesteijn1",
Algorithm::Markesteijn3 => "Markesteijn3",
Algorithm::Dht => "DHT",
_ => unreachable!(),
},
cfa: "Quad 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",
Algorithm::Vng => "VNG",
Algorithm::QuadPpg => "Quad-PPG",
_ => 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];
}
}
pub fn bin2x2(
input: &[f32],
width: usize,
height: usize,
output: &mut [f32],
) -> Result<(), DemosaicError> {
quad_bayer::binning::bin2x2(input, width, height, output)
}
pub fn demosaic_quad_binned(
input: &[f32],
width: usize,
height: usize,
cfa: &CfaPattern,
algorithm: BayerAlgorithm,
output: &mut [f32],
) -> Result<(), DemosaicError> {
quad_bayer::binning::demosaic_quad_binned(input, width, height, cfa, algorithm, output)
}
pub fn demosaic_quad_binned_interleaved(
input: &[f32],
width: usize,
height: usize,
cfa: &CfaPattern,
algorithm: BayerAlgorithm,
output: &mut [f32],
) -> Result<(), DemosaicError> {
quad_bayer::binning::demosaic_quad_binned_interleaved(input, width, height, cfa, algorithm, output)
}
pub fn remosaic(
input: &[f32],
width: usize,
height: usize,
quad_cfa: &CfaPattern,
output: &mut [f32],
) -> Result<(), DemosaicError> {
remosaic::remosaic(input, width, height, quad_cfa, output)
}