use crate::opbasics::*;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OpDemosaic {
pub cfa: String,
}
impl OpDemosaic {
pub fn new(img: &RawImage) -> OpDemosaic {
OpDemosaic{
cfa: img.cropped_cfa().to_string(),
}
}
}
impl<'a> ImageOp<'a> for OpDemosaic {
fn name(&self) -> &str {"demosaic"}
fn run(&self, pipeline: &PipelineGlobals, buf: Arc<OpBuffer>) -> Arc<OpBuffer> {
let (scale, nwidth, nheight) = if pipeline.settings.maxwidth == 0 || pipeline.settings.maxheight == 0 {
(1.0, buf.width, buf.height)
} else {
let xscale = (buf.width as f32) / (pipeline.settings.maxwidth as f32);
let yscale = (buf.height as f32) / (pipeline.settings.maxheight as f32);
if yscale > xscale {
(yscale, ((buf.width as f32)/yscale) as usize, pipeline.settings.maxheight)
} else {
(xscale, pipeline.settings.maxwidth, ((buf.height as f32)/xscale) as usize)
}
};
let cfa = CFA::new(&self.cfa);
let minscale = match cfa.width {
2 => 2.0,
6 => 3.0,
8 => 2.0,
12 => 12.0,
_ => 2.0,
};
if scale <= 1.0 && buf.colors == 4 {
buf
} else if buf.colors == 4 {
Arc::new(scale_down(&buf, nwidth, nheight))
} else if scale >= minscale {
Arc::new(scaled(cfa, &buf, nwidth, nheight))
} else {
let fullsize = full(cfa, &buf);
if scale > 1.0 {
Arc::new(scale_down(&fullsize, nwidth, nheight))
} else {
Arc::new(fullsize)
}
}
}
}
pub fn full(cfa: CFA, buf: &OpBuffer) -> OpBuffer {
let mut out = OpBuffer::new(buf.width, buf.height, 4, buf.monochrome);
let offsets3x3: [(isize,isize);9] = [
(-1,-1), (-1, 0), (-1, 1),
( 0,-1), ( 0, 0), ( 0, 1),
( 1,-1), ( 1, 0), ( 1, 1),
];
let mut lookups = [[[0;9];48];48];
for (row, line) in lookups.iter_mut().enumerate() {
for (col, colors) in line.iter_mut().enumerate() {
let pixcolor = cfa.color_at(row, col);
for (i, o) in offsets3x3.iter().enumerate() {
let (dy, dx) = *o;
let row = (48+dy) as usize + row;
let col = (48+dx) as usize + col;
let ocolor = cfa.color_at(row, col);
colors[i] = if ocolor != pixcolor || (dx == 0 && dy == 0) { ocolor } else { 4 };
}
}
}
out.mutate_lines(&(|line: &mut [f32], row| {
for (col, pix) in line.chunks_exact_mut(4).enumerate() {
let ref colors = lookups[row%48][col%48];
let mut sums = [0f32;5];
let mut counts = [0f32;5];
for (i, o) in offsets3x3.iter().enumerate() {
let (dy, dx) = *o;
let row = row as isize + dy;
let col = col as isize + dx;
if row >= 0 && row < (buf.height as isize) &&
col >= 0 && col < (buf.width as isize) {
sums[colors[i]] += buf.data[(row as usize)*buf.width+(col as usize)];
counts[colors[i]] += 1.0;
}
}
for c in 0..4 {
if counts[c] > 0.0 {
pix[c] = sums[c] / counts[c];
}
}
}
}));
out
}
fn calc_skips(idx: usize, idxmax: usize, skip: f32) -> (usize, usize, f32, f32) {
let from = (idx as f32)*skip;
let fromback = from.floor();
let fromfactor = 1.0 - (from-fromback).fract();
let to = ((idx+1) as f32)*skip;
let toforward = (idxmax as f32).min(to.ceil());
let tofactor = (toforward-to).fract();
(fromback as usize, toforward as usize, fromfactor, tofactor)
}
pub fn scaled(cfa: CFA, buf: &OpBuffer, nwidth: usize, nheight: usize) -> OpBuffer {
let mut out = OpBuffer::new(nwidth, nheight, 4, buf.monochrome);
let rowskip = (buf.width as f32) / (nwidth as f32);
let colskip = (buf.height as f32) / (nheight as f32);
out.mutate_lines(&(|line: &mut [f32], row| {
for col in 0..nwidth {
let mut sums: [f32; 4] = [0.0;4];
let mut counts: [f32; 4] = [0.0;4];
let (fromrow, torow, topfactor, bottomfactor) = calc_skips(row, buf.height, rowskip);
for y in fromrow..torow {
let (fromcol, tocol, leftfactor, rightfactor) = calc_skips(col, buf.width, colskip);
for x in fromcol..tocol {
let factor = {
(if y == fromrow {topfactor} else if y == torow {bottomfactor} else {1.0}) *
(if x == fromcol {leftfactor} else if x == tocol {rightfactor} else {1.0})
};
let c = cfa.color_at(y, x);
sums[c] += (buf.data[y*buf.width+x] as f32) * factor;
counts[c] += factor;
}
}
for c in 0..4 {
if counts[c] > 0.0 {
line[col*4+c] = sums[c] / counts[c];
}
}
}
}));
out
}
pub fn scale_down(buf: &OpBuffer, nwidth: usize, nheight: usize) -> OpBuffer {
assert_eq!(buf.colors, 4);
let mut out = OpBuffer::new(nwidth, nheight, 4, buf.monochrome);
let rowskip = (buf.width as f32) / (nwidth as f32);
let colskip = (buf.height as f32) / (nheight as f32);
out.mutate_lines(&(|line: &mut [f32], row| {
for col in 0..nwidth {
let mut sums: [f32; 4] = [0.0;4];
let mut counts: [f32; 4] = [0.0;4];
let (fromrow, torow, topfactor, bottomfactor) = calc_skips(row, buf.height, rowskip);
for y in fromrow..torow {
let (fromcol, tocol, leftfactor, rightfactor) = calc_skips(col, buf.width, colskip);
for x in fromcol..tocol {
let factor = {
(if y == fromrow {topfactor} else if y == torow {bottomfactor} else {1.0}) *
(if x == fromcol {leftfactor} else if x == tocol {rightfactor} else {1.0})
};
for c in 0..4 {
sums[c] += buf.data[(y*buf.width+x)*4 + c] * factor;
counts[c] += factor;
}
}
}
for c in 0..4 {
if counts[c] > 0.0 {
line[col*4+c] = sums[c] / counts[c];
}
}
}
}));
out
}