Skip to main content

bayer/demosaic/
cubic.rs

1//! Demosaicing using cubic interpolation.
2//!
3//! ```text
4//!   green_kernel = (1 / 256) *
5//!       [   0   0   0   1   0   0   0
6//!       ;   0   0  -9   0  -9   0   0
7//!       ;   0  -9   0  81   0  -9   0
8//!       ;   1   0  81 256  81   0   1
9//!       ;   0  -9   0  81   0  -9   0
10//!       ;   0   0  -9   0  -9   0   0
11//!       ;   0   0   0   1   0   0   0 ];
12//!
13//!   red/blue_kernel = (1 / 256) *
14//!       [   1   0  -9 -16  -9   0   1
15//!       ;   0   0   0   0   0   0   0
16//!       ;  -9   0  81 144  81   0  -9
17//!       ; -16   0 144 256 144   0 -16
18//!       ;  -9   0  81 144  81   0  -9
19//!       ;   0   0   0   0   0   0   0
20//!       ;   1   0  -9 -16  -9   0   1 ];
21//! ```
22
23use std::cmp::min;
24use std::io::Read;
25
26#[cfg(feature = "rayon")]
27use std::slice;
28
29#[cfg(feature = "rayon")]
30use rayon::prelude::*;
31
32use ::{BayerDepth,BayerError,BayerResult,CFA,RasterMut};
33use bayer::{BayerRead8,BayerRead16};
34use border_mirror::*;
35use demosaic::check_depth;
36
37const PADDING: usize = 3;
38
39pub fn run(r: &mut Read,
40        depth: BayerDepth, cfa: CFA, dst: &mut RasterMut)
41        -> BayerResult<()> {
42    if dst.w < 4 || dst.h < 4 {
43        return Err(BayerError::WrongResolution);
44    }
45    if !check_depth(depth, dst.depth) {
46        return Err(BayerError::WrongDepth);
47    }
48
49    match depth {
50        BayerDepth::Depth8 => debayer_u8(r, cfa, dst),
51        BayerDepth::Depth16BE => debayer_u16(r, true, cfa, dst),
52        BayerDepth::Depth16LE => debayer_u16(r, false, cfa, dst),
53    }
54}
55
56macro_rules! apply_kernel_row {
57    ($T:ident; $row:ident,
58            $prv3:expr, $prv2:expr, $prv1:expr, $curr:expr,
59            $nxt1:expr, $nxt2:expr, $nxt3:expr,
60            $cfa:expr, $w:expr) => {{
61        let (mut i, cfa_c, cfa_g) =
62            if $cfa == CFA::BGGR || $cfa == CFA::RGGB {
63                (0, $cfa, $cfa.next_x())
64            } else {
65                apply_kernel_g!($T; $row, $w, $prv3, $prv2, $prv1, $curr, $nxt1, $nxt2, $nxt3, $cfa, 0);
66                (1, $cfa.next_x(), $cfa)
67            };
68
69        while i + 1 < $w {
70            apply_kernel_c!($T; $row, $w, $prv3, $prv2, $prv1, $curr, $nxt1, $nxt2, $nxt3, cfa_c, i);
71            apply_kernel_g!($T; $row, $w, $prv3, $prv2, $prv1, $curr, $nxt1, $nxt2, $nxt3, cfa_g, i + 1);
72            i = i + 2;
73        }
74
75        if i < $w {
76            apply_kernel_c!($T; $row, $w, $prv3, $prv2, $prv1, $curr, $nxt1, $nxt2, $nxt3, cfa_c, i);
77        }
78    }}
79}
80
81macro_rules! apply_kernel_c {
82    ($T:ident; $row:ident, $w:expr,
83            $prv3:expr, $prv2:expr, $prv1:expr, $curr:expr,
84            $nxt1:expr, $nxt2:expr, $nxt3:expr,
85            $cfa:expr, $i:expr) => {{
86        // current = B/R, diagonal = R/B.
87        let (c, d) = if $cfa == CFA::BGGR { (2, 0) } else { (0, 2) };
88        let j = $i + PADDING;
89
90        let g_pos
91            = (   $prv1[j] as u32
92                  + $curr[j - 1] as u32 + $curr[j + 1] as u32
93                  + $nxt1[j] as u32) * 81
94            + (   $prv3[j] as u32
95                  + $curr[j - 3] as u32 + $curr[j + 3] as u32
96                  + $nxt3[j] as u32);
97        let g_neg
98            = (   $prv2[j - 1] as u32 + $prv2[j + 1] as u32
99                  + $prv1[j - 2] as u32 + $prv1[j + 2] as u32
100                  + $nxt1[j - 2] as u32 + $nxt1[j + 2] as u32
101                  + $nxt2[j - 1] as u32 + $nxt2[j + 1] as u32) * 9;
102
103        let d_pos
104            = (   $prv1[j - 1] as u32 + $prv1[j + 1] as u32
105                  + $nxt1[j - 1] as u32 + $nxt1[j + 1] as u32) * 81
106            + (   $prv3[j - 3] as u32 + $prv3[j + 3] as u32
107                  + $nxt3[j - 3] as u32 + $nxt3[j + 3] as u32);
108        let d_neg
109            = (   $prv3[j - 1] as u32 + $prv3[j + 1] as u32
110                  + $prv1[j - 3] as u32 + $prv1[j + 3] as u32
111                  + $nxt1[j - 3] as u32 + $nxt1[j + 3] as u32
112                  + $nxt3[j - 1] as u32 + $nxt3[j + 1] as u32) * 9;
113
114        $row[3 * $i + c] = $curr[j];
115        $row[3 * $i + 1]
116            = min(g_pos.saturating_sub(g_neg) / 256,
117                    $T::max_value() as u32) as $T;
118        $row[3 * $i + d]
119            = min(d_pos.saturating_sub(d_neg) / 256,
120                    $T::max_value() as u32) as $T;
121    }}
122}
123
124macro_rules! apply_kernel_g {
125    ($T:ident; $row:ident, $w:expr,
126            $prv3:expr, $prv2:expr, $prv1:expr, $curr:expr,
127            $nxt1:expr, $nxt2:expr, $nxt3:expr,
128            $cfa:expr, $i:expr) => {{
129        // horizontal = B/R, vertical = R/G.
130        let (h, v) = if $cfa == CFA::GBRG { (2, 0) } else { (0, 2) };
131        let j = $i + PADDING;
132
133        let h_pos = ($curr[j - 1] as u32 + $curr[j + 1] as u32) * 9;
134        let h_neg = ($curr[j - 3] as u32 + $curr[j + 3] as u32);
135        let v_pos = ($prv1[j] as u32 + $nxt1[j] as u32) * 9;
136        let v_neg = ($prv3[j] as u32 + $nxt3[j] as u32);
137
138        $row[3 * $i + h]
139            = min(h_pos.saturating_sub(h_neg) / 16,
140                    $T::max_value() as u32) as $T;
141        $row[3 * $i + 1] = $curr[j];
142        $row[3 * $i + v]
143            = min(v_pos.saturating_sub(v_neg) / 16,
144                    $T::max_value() as u32) as $T;
145    }}
146}
147
148/*--------------------------------------------------------------*/
149/* Rayon                                                        */
150/*--------------------------------------------------------------*/
151
152#[cfg(feature = "rayon")]
153#[allow(unused_parens)]
154fn debayer_u8(r: &mut Read, cfa: CFA, dst: &mut RasterMut)
155        -> BayerResult<()> {
156    let (w, h) = (dst.w, dst.h);
157    let mut data = vec![0u8; (2 * PADDING + w) * (2 * PADDING + h)];
158
159    // Read all data.
160    {
161        let stride = 2 * PADDING + w;
162        let rdr = BorderMirror8::new(w, PADDING);
163        for mut row in data.chunks_mut(stride).skip(PADDING).take(h) {
164            rdr.read_line(r, &mut row)?;
165        }
166
167        {
168            let (top, src) = data.split_at_mut(stride * PADDING);
169            top[(stride * 0)..(stride * 1)].copy_from_slice(&src[(stride * 3)..(stride * 4)]);
170            top[(stride * 1)..(stride * 2)].copy_from_slice(&src[(stride * 2)..(stride * 3)]);
171            top[(stride * 2)..(stride * 3)].copy_from_slice(&src[(stride * 1)..(stride * 2)]);
172        }
173
174        {
175            let (src, bottom) = data.split_at_mut(stride * (h + PADDING));
176            let yy = PADDING + h;
177            bottom[(stride * 0)..(stride * 1)].copy_from_slice(&src[(stride * (yy - 2))..(stride * (yy - 1))]);
178            bottom[(stride * 1)..(stride * 2)].copy_from_slice(&src[(stride * (yy - 3))..(stride * (yy - 2))]);
179            bottom[(stride * 2)..(stride * 3)].copy_from_slice(&src[(stride * (yy - 4))..(stride * (yy - 3))]);
180        }
181    }
182
183    dst.buf.par_chunks_mut(dst.stride).enumerate()
184            .for_each(|(y, mut row)| {
185        let stride = 2 * PADDING + w;
186        let prv3 = &data[(stride * (PADDING + y - 3)) .. (stride * (PADDING + y - 2))];
187        let prv2 = &data[(stride * (PADDING + y - 2)) .. (stride * (PADDING + y - 1))];
188        let prv1 = &data[(stride * (PADDING + y - 1)) .. (stride * (PADDING + y + 0))];
189        let curr = &data[(stride * (PADDING + y + 0)) .. (stride * (PADDING + y + 1))];
190        let nxt1 = &data[(stride * (PADDING + y + 1)) .. (stride * (PADDING + y + 2))];
191        let nxt2 = &data[(stride * (PADDING + y + 2)) .. (stride * (PADDING + y + 3))];
192        let nxt3 = &data[(stride * (PADDING + y + 3)) .. (stride * (PADDING + y + 4))];
193        let cfa_y = if y % 2 == 0 { cfa } else { cfa.next_y() };
194
195        apply_kernel_row!(u8; row, prv3, prv2, prv1, curr, nxt1, nxt2, nxt3, cfa_y, w);
196    });
197
198    Ok(())
199}
200
201#[cfg(feature = "rayon")]
202#[allow(unused_parens)]
203fn debayer_u16(r: &mut Read, be: bool, cfa: CFA, dst: &mut RasterMut)
204        -> BayerResult<()> {
205    let (w, h) = (dst.w, dst.h);
206    let mut data = vec![0u16; (2 * PADDING + w) * (2 * PADDING + h)];
207
208    // Read all data.
209    {
210        let stride = 2 * PADDING + w;
211        let rdr: Box<BayerRead16> = if be {
212            Box::new(BorderMirror16BE::new(w, PADDING))
213        } else {
214            Box::new(BorderMirror16LE::new(w, PADDING))
215        };
216
217        for mut row in data.chunks_mut(stride).skip(PADDING).take(h) {
218            rdr.read_line(r, &mut row)?;
219        }
220
221        {
222            let (top, src) = data.split_at_mut(stride * PADDING);
223            top[(stride * 0)..(stride * 1)].copy_from_slice(&src[(stride * 3)..(stride * 4)]);
224            top[(stride * 1)..(stride * 2)].copy_from_slice(&src[(stride * 2)..(stride * 3)]);
225            top[(stride * 2)..(stride * 3)].copy_from_slice(&src[(stride * 1)..(stride * 2)]);
226        }
227
228        {
229            let (src, bottom) = data.split_at_mut(stride * (h + PADDING));
230            let yy = PADDING + h;
231            bottom[(stride * 0)..(stride * 1)].copy_from_slice(&src[(stride * (yy - 2))..(stride * (yy - 1))]);
232            bottom[(stride * 1)..(stride * 2)].copy_from_slice(&src[(stride * (yy - 3))..(stride * (yy - 2))]);
233            bottom[(stride * 2)..(stride * 3)].copy_from_slice(&src[(stride * (yy - 4))..(stride * (yy - 3))]);
234        }
235    }
236
237    dst.buf.par_chunks_mut(dst.stride).enumerate()
238            .for_each(|(y, mut row)| {
239        let stride = 2 * PADDING + w;
240        let prv3 = &data[(stride * (PADDING + y - 3)) .. (stride * (PADDING + y - 2))];
241        let prv2 = &data[(stride * (PADDING + y - 2)) .. (stride * (PADDING + y - 1))];
242        let prv1 = &data[(stride * (PADDING + y - 1)) .. (stride * (PADDING + y + 0))];
243        let curr = &data[(stride * (PADDING + y + 0)) .. (stride * (PADDING + y + 1))];
244        let nxt1 = &data[(stride * (PADDING + y + 1)) .. (stride * (PADDING + y + 2))];
245        let nxt2 = &data[(stride * (PADDING + y + 2)) .. (stride * (PADDING + y + 3))];
246        let nxt3 = &data[(stride * (PADDING + y + 3)) .. (stride * (PADDING + y + 4))];
247        let cfa_y = if y % 2 == 0 { cfa } else { cfa.next_y() };
248
249        let row16 = unsafe{ slice::from_raw_parts_mut(row.as_mut_ptr() as *mut u16, row.len() / 2) };
250        apply_kernel_row!(u16; row16, prv3, prv2, prv1, curr, nxt1, nxt2, nxt3, cfa_y, w);
251    });
252
253    Ok(())
254}
255
256/*--------------------------------------------------------------*/
257/* Naive                                                        */
258/*--------------------------------------------------------------*/
259
260#[cfg(not(feature = "rayon"))]
261#[allow(unused_parens)]
262fn debayer_u8(r: &mut Read, cfa: CFA, dst: &mut RasterMut)
263        -> BayerResult<()> {
264    let (w, h) = (dst.w, dst.h);
265    let mut prv3 = vec![0u8; 2 * PADDING + w];
266    let mut prv2 = vec![0u8; 2 * PADDING + w];
267    let mut prv1 = vec![0u8; 2 * PADDING + w];
268    let mut curr = vec![0u8; 2 * PADDING + w];
269    let mut nxt1 = vec![0u8; 2 * PADDING + w];
270    let mut nxt2 = vec![0u8; 2 * PADDING + w];
271    let mut nxt3 = vec![0u8; 2 * PADDING + w];
272    let mut cfa = cfa;
273
274    let rdr = BorderMirror8::new(w, PADDING);
275    rdr.read_line(r, &mut curr)?;
276    rdr.read_line(r, &mut nxt1)?;
277    rdr.read_line(r, &mut nxt2)?;
278    rdr.read_line(r, &mut nxt3)?;
279
280    prv1.copy_from_slice(&nxt1);
281    prv2.copy_from_slice(&nxt2);
282    prv3.copy_from_slice(&nxt3);
283
284    {   // y = 0.
285        let row = dst.borrow_row_u8_mut(0);
286        apply_kernel_row!(u8; row, nxt3, nxt2, nxt1, curr, nxt1, nxt2, nxt3, cfa, w);
287        cfa = cfa.next_y();
288    }
289
290    for y in 1..(h - 3) {
291        rotate!(prv3 <- prv2 <- prv1 <- curr <- nxt1 <- nxt2 <- nxt3);
292        rdr.read_line(r, &mut nxt3)?;
293
294        let row = dst.borrow_row_u8_mut(y);
295        apply_kernel_row!(u8; row, prv3, prv2, prv1, curr, nxt1, nxt2, nxt3, cfa, w);
296        cfa = cfa.next_y();
297    }
298
299    {   // y = h - 3.
300        let row = dst.borrow_row_u8_mut(h - 3);
301        apply_kernel_row!(u8; row, prv2, prv1, curr, nxt1, nxt2, nxt3, nxt2, cfa, w);
302        cfa = cfa.next_y();
303    }
304
305    {   // y = h - 2.
306        let row = dst.borrow_row_u8_mut(h - 2);
307        apply_kernel_row!(u8; row, prv1, curr, nxt1, nxt2, nxt3, nxt2, nxt1, cfa, w);
308        cfa = cfa.next_y();
309    }
310
311    {   // y = h - 1.
312        let row = dst.borrow_row_u8_mut(h - 1);
313        apply_kernel_row!(u8; row, curr, nxt1, nxt2, nxt3, nxt2, nxt1, curr, cfa, w);
314    }
315
316    Ok(())
317}
318
319#[cfg(not(feature = "rayon"))]
320#[allow(unused_parens)]
321fn debayer_u16(r: &mut Read, be: bool, cfa: CFA, dst: &mut RasterMut)
322        -> BayerResult<()> {
323    let (w, h) = (dst.w, dst.h);
324    let mut prv3 = vec![0u16; 2 * PADDING + w];
325    let mut prv2 = vec![0u16; 2 * PADDING + w];
326    let mut prv1 = vec![0u16; 2 * PADDING + w];
327    let mut curr = vec![0u16; 2 * PADDING + w];
328    let mut nxt1 = vec![0u16; 2 * PADDING + w];
329    let mut nxt2 = vec![0u16; 2 * PADDING + w];
330    let mut nxt3 = vec![0u16; 2 * PADDING + w];
331    let mut cfa = cfa;
332
333    let rdr: Box<BayerRead16> = if be {
334        Box::new(BorderMirror16BE::new(w, PADDING))
335    } else {
336        Box::new(BorderMirror16LE::new(w, PADDING))
337    };
338    rdr.read_line(r, &mut curr)?;
339    rdr.read_line(r, &mut nxt1)?;
340    rdr.read_line(r, &mut nxt2)?;
341    rdr.read_line(r, &mut nxt3)?;
342
343    prv1.copy_from_slice(&nxt1);
344    prv2.copy_from_slice(&nxt2);
345    prv3.copy_from_slice(&nxt3);
346
347    {   // y = 0.
348        let row = dst.borrow_row_u16_mut(0);
349        apply_kernel_row!(u16; row, nxt3, nxt2, nxt1, curr, nxt1, nxt2, nxt3, cfa, w);
350        cfa = cfa.next_y();
351    }
352
353    for y in 1..(h - 3) {
354        rotate!(prv3 <- prv2 <- prv1 <- curr <- nxt1 <- nxt2 <- nxt3);
355        rdr.read_line(r, &mut nxt3)?;
356
357        let row = dst.borrow_row_u16_mut(y);
358        apply_kernel_row!(u16; row, prv3, prv2, prv1, curr, nxt1, nxt2, nxt3, cfa, w);
359        cfa = cfa.next_y();
360    }
361
362    {   // y = h - 3.
363        let row = dst.borrow_row_u16_mut(h - 3);
364        apply_kernel_row!(u16; row, prv2, prv1, curr, nxt1, nxt2, nxt3, nxt2, cfa, w);
365        cfa = cfa.next_y();
366    }
367
368    {   // y = h - 2.
369        let row = dst.borrow_row_u16_mut(h - 2);
370        apply_kernel_row!(u16; row, prv1, curr, nxt1, nxt2, nxt3, nxt2, nxt1, cfa, w);
371        cfa = cfa.next_y();
372    }
373
374    {   // y = h - 1.
375        let row = dst.borrow_row_u16_mut(h - 1);
376        apply_kernel_row!(u16; row, curr, nxt1, nxt2, nxt3, nxt2, nxt1, curr, cfa, w);
377    }
378
379    Ok(())
380}
381
382#[cfg(test)]
383mod tests {
384    use std::io::Cursor;
385    use ::{CFA,RasterDepth,RasterMut};
386    use super::debayer_u8;
387
388    #[test]
389    fn test_even() {
390        // R: set.seed(0); matrix(floor(runif(n=64, min=0, max=256)), nrow=8, byrow=TRUE)
391        let src = [
392            229, 67, 95,146,232, 51,229,241,
393            169,161, 15, 52, 45,175, 98,197,
394            127,183,253, 97,199,239, 54,166,
395             32, 68, 98,  3, 97,222, 87,123,
396            153,126, 47,211,171,203, 27,185,
397            105,210,165,200,141,135,202,  5,
398            122,187,177,122,220,112, 62, 18,
399             25, 80,132,169,104,233, 75,117 ];
400
401        let expected = [
402			229,122,186,  161, 67,172,   95, 43,108,  155,146, 58,  232, 61,104,  239, 51,169,  229,117,196,  228,241,206,
403			182,169,174,  177,110,161,  177, 15, 98,  201, 70, 52,  219, 45,105,  189,104,175,  154, 98,195,  145,159,197,
404			127,159,116,  185,183,105,  253, 95, 48,  242, 97, 15,  199,121,106,  123,239,203,   54,153,195,   35,166,167,
405			135, 32, 76,  140,103, 68,  151, 98, 21,  176,121,  3,  179, 97,114,  105,159,222,   27, 87,180,    8,115,123,
406			153, 80,146,   98,126,141,   47,157,116,  111,211,100,  171,168,142,  106,203,175,   27,179,110,    9,185, 52,
407			139,105,211,  115,154,210,   99,165,209,  153,167,200,  193,141,175,  124,179,135,   42,202, 57,   23,160,  5,
408			122,118,139,  143,187,145,  177,158,170,  211,122,194,  220,110,200,  143,112,184,   62, 94,114,   42, 18, 60,
409			118, 25, 68,  148,129, 80,  193,132,120,  224,111,169,  226,104,213,  148, 95,233,   66, 75,171,   46, 16,117 ];
410
411        const IMG_W: usize = 8;
412        const IMG_H: usize = 8;
413        let mut buf = [0u8; 3 * IMG_W * IMG_H];
414
415        let res = debayer_u8(&mut Cursor::new(&src[..]), CFA::RGGB,
416                &mut RasterMut::new(IMG_W, IMG_H, RasterDepth::Depth8, &mut buf));
417        assert!(res.is_ok());
418        assert_eq!(&buf[..], &expected[..]);
419    }
420
421    #[test]
422    fn test_odd() {
423        // R: set.seed(0); matrix(floor(runif(n=49, min=0, max=256)), nrow=7, byrow=TRUE)
424        let src = [
425            229, 67, 95,146,232, 51,229,
426            241,169,161, 15, 52, 45,175,
427             98,197,127,183,253, 97,199,
428            239, 54,166, 32, 68, 98,  3,
429             97,222, 87,123,153,126, 47,
430            211,171,203, 27,185,105,210,
431            165,200,141,135,202,  5,122 ];
432
433        let expected = [
434			229,147,204,  161, 67,183,   95,123, 96,  155,146, 12,  232, 52, 14,  238, 51, 38,  229,123, 41,
435			171,241,188,  136,163,169,  111,161, 90,  177,144, 15,  247, 52, 20,  243, 93, 45,  225,175, 48,
436			 98,236,114,  102,197,104,  127,185, 61,  195,183, 23,  253, 95, 42,  230, 97, 71,  199, 99, 76,
437			 85,239, 56,   88,208, 54,  105,166, 38,  160,129, 32,  201, 68, 63,  159, 53, 98,  116,  3,106,
438			 97,231,114,   88,222,105,   87,178, 63,  126,123, 30,  153,125, 63,   97,126,104,   47,124,114,
439			135,211,189,  122,214,171,  114,203, 94,  149,165, 27,  174,185, 57,  124,138,105,   79,210,114,
440			165,203,205,  150,200,185,  141,184,101,  175,135, 26,  202,116, 56,  160,  5,105,  122, 93,115 ];
441
442        const IMG_W: usize = 7;
443        const IMG_H: usize = 7;
444        let mut buf = [0u8; 3 * IMG_W * IMG_H];
445
446        let res = debayer_u8(&mut Cursor::new(&src[..]), CFA::RGGB,
447                &mut RasterMut::new(IMG_W, IMG_H, RasterDepth::Depth8, &mut buf));
448        assert!(res.is_ok());
449        assert_eq!(&buf[..], &expected[..]);
450    }
451
452    #[test]
453    fn test_overflow() {
454        let src = [
455            255,255,255,255,255,255,255,
456            255,255,255,255,255,255,255,
457            255,255,255,  0,255,255,255,
458            255,255,  0,  0,  0,255,255,
459            255,255,255,  0,255,255,255,
460            255,255,255,255,255,255,255,
461            255,255,255,255,255,255,255 ];
462
463        let expected = [
464			255,255,251,  255,255,255,  255,255,255,  255,255,255,  255,255,255,  255,255,255,  255,255,251,
465			255,255,255,  255,255,255,  255,255,255,  255,190,255,  255,255,255,  255,255,255,  255,255,255,
466			255,255,255,  255,255,255,  255,111,174,  255,  0,111,  255,111,174,  255,255,255,  255,255,255,
467			255,255,255,  255,190,255,  255,  0,111,  255,  0,  0,  255,  0,111,  255,190,255,  255,255,255,
468			255,255,255,  255,255,255,  255,111,174,  255,  0,111,  255,111,174,  255,255,255,  255,255,255,
469			255,255,255,  255,255,255,  255,255,255,  255,190,255,  255,255,255,  255,255,255,  255,255,255,
470			255,255,251,  255,255,255,  255,255,255,  255,255,255,  255,255,255,  255,255,255,  255,255,251 ];
471
472        const IMG_W: usize = 7;
473        const IMG_H: usize = 7;
474        let mut buf = [0u8; 3 * IMG_W * IMG_H];
475
476        let res = debayer_u8(&mut Cursor::new(&src[..]), CFA::RGGB,
477                &mut RasterMut::new(IMG_W, IMG_H, RasterDepth::Depth8, &mut buf));
478        assert!(res.is_ok());
479        assert_eq!(&buf[..], &expected[..]);
480    }
481}