1use super::{Demosaic, DemosaicError};
7use crate::core::image::{CfaPattern, RawImage};
8use rayon::prelude::*;
9
10pub struct Amaze;
28
29impl Demosaic for Amaze {
30 fn demosaic_into(&self, raw: &RawImage, output: &mut [u16]) -> Result<(), DemosaicError> {
31 let width = raw.active_area().size.width as usize;
32 let height = raw.active_area().size.height as usize;
33 let x_off = raw.active_area().origin.x as usize;
34 let y_off = raw.active_area().origin.y as usize;
35 let raw_w = raw.width() as usize;
36
37 let expected_size = width * height * 3;
38 if output.len() != expected_size {
39 return Err(DemosaicError::BufferSizeMismatch {
40 expected: expected_size,
41 actual: output.len(),
42 });
43 }
44
45 if width < 6 || height < 6 {
46 return Err(DemosaicError::InvalidDimensions);
47 }
48
49 let white = raw.white_level() as f32;
50
51 let fc = |x: usize, y: usize| -> u8 {
53 let ax = x + x_off;
54 let ay = y + y_off;
55 match raw.cfa_pattern() {
56 CfaPattern::Rggb => match (ax % 2, ay % 2) {
57 (0, 0) => 0, (1, 0) => 1, (0, 1) => 3, _ => 2, },
62 CfaPattern::Grbg => match (ax % 2, ay % 2) {
63 (0, 0) => 1,
64 (1, 0) => 0,
65 (0, 1) => 2,
66 _ => 3,
67 },
68 CfaPattern::Gbrg => match (ax % 2, ay % 2) {
69 (0, 0) => 3,
70 (1, 0) => 2,
71 (0, 1) => 0,
72 _ => 1,
73 },
74 CfaPattern::Bggr => match (ax % 2, ay % 2) {
75 (0, 0) => 2,
76 (1, 0) => 3,
77 (0, 1) => 1,
78 _ => 0,
79 },
80 }
81 };
82
83 let get = |x: isize, y: isize| -> f32 {
85 let cx = x.clamp(0, (width as isize) - 1) as usize;
86 let cy = y.clamp(0, (height as isize) - 1) as usize;
87 raw.data[(cy + y_off) * raw_w + (cx + x_off)] as f32
88 };
89
90 let mut green = vec![0.0f32; width * height];
94
95 for y in 0..height {
97 for x in 0..width {
98 let color = fc(x, y);
99 let ix = x as isize;
100 let iy = y as isize;
101
102 if color == 1 || color == 3 {
103 green[y * width + x] = get(ix, iy);
105 } else {
106 let dh = (get(ix - 1, iy) - get(ix + 1, iy)).abs()
110 + (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy)).abs();
111
112 let dv = (get(ix, iy - 1) - get(ix, iy + 1)).abs()
114 + (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2)).abs();
115
116 let gh = (get(ix - 1, iy) + get(ix + 1, iy)) * 0.5
118 + (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy)) * 0.25;
119
120 let gv = (get(ix, iy - 1) + get(ix, iy + 1)) * 0.5
122 + (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2)) * 0.25;
123
124 let eps = 1e-5;
126 if dh < dv * 0.5 {
127 green[y * width + x] = gh;
129 } else if dv < dh * 0.5 {
130 green[y * width + x] = gv;
132 } else {
133 let wh = 1.0 / (dh + eps);
135 let wv = 1.0 / (dv + eps);
136 green[y * width + x] = (wh * gh + wv * gv) / (wh + wv);
137 }
138
139 green[y * width + x] = green[y * width + x].max(0.0);
141 }
142 }
143 }
144
145 let mut gh_plane = vec![0.0f32; width * height];
149 let mut gv_plane = vec![0.0f32; width * height];
150
151 for y in 0..height {
152 for x in 0..width {
153 let color = fc(x, y);
154 let ix = x as isize;
155 let iy = y as isize;
156
157 if color == 1 || color == 3 {
158 gh_plane[y * width + x] = get(ix, iy);
159 gv_plane[y * width + x] = get(ix, iy);
160 } else {
161 gh_plane[y * width + x] = (get(ix - 1, iy) + get(ix + 1, iy)) * 0.5
162 + (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy)) * 0.25;
163 gv_plane[y * width + x] = (get(ix, iy - 1) + get(ix, iy + 1)) * 0.5
164 + (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2)) * 0.25;
165 }
166 }
167 }
168
169 let mut h_homo = vec![0i32; width * height];
171 let mut v_homo = vec![0i32; width * height];
172
173 let border = 3usize;
174 for y in border..height.saturating_sub(border) {
175 for x in border..width.saturating_sub(border) {
176 let color = fc(x, y);
177 if color == 1 || color == 3 {
178 continue;
179 }
180
181 let mut hh = 0i32;
182 let mut vh = 0i32;
183
184 for dy in -1i32..=1 {
185 for dx in -1i32..=1 {
186 let nx = (x as i32 + dx) as usize;
187 let ny = (y as i32 + dy) as usize;
188 let idx = ny * width + nx;
189
190 let cdh = (gh_plane[idx] - get(nx as isize, ny as isize)).abs();
192 let cdv = (gv_plane[idx] - get(nx as isize, ny as isize)).abs();
194
195 let lh = (gh_plane[idx] - gh_plane[y * width + x]).abs();
197 let lv = (gv_plane[idx] - gv_plane[y * width + x]).abs();
198
199 let eps_h = cdh + lh;
200 let eps_v = cdv + lv;
201
202 if eps_h < eps_v {
203 hh += 1;
204 } else if eps_v < eps_h {
205 vh += 1;
206 }
207 }
208 }
209
210 h_homo[y * width + x] = hh;
211 v_homo[y * width + x] = vh;
212 }
213 }
214
215 for y in border..height.saturating_sub(border) {
217 for x in border..width.saturating_sub(border) {
218 let color = fc(x, y);
219 if color == 1 || color == 3 {
220 continue;
221 }
222
223 let idx = y * width + x;
224 let hh = h_homo[idx];
225 let vh = v_homo[idx];
226
227 if hh > vh + 1 {
228 green[idx] = gh_plane[idx];
229 } else if vh > hh + 1 {
230 green[idx] = gv_plane[idx];
231 } else {
232 let wh = (hh + 1) as f32;
234 let wv = (vh + 1) as f32;
235 green[idx] = (wh * gh_plane[idx] + wv * gv_plane[idx]) / (wh + wv);
236 }
237
238 green[idx] = green[idx].max(0.0);
239 }
240 }
241
242 drop(gh_plane);
244 drop(gv_plane);
245 drop(h_homo);
246 drop(v_homo);
247
248 let mut red = vec![0.0f32; width * height];
255 let mut blue = vec![0.0f32; width * height];
256
257 let mut cd_rg = vec![0.0f32; width * height]; let mut cd_bg = vec![0.0f32; width * height]; for y in 0..height {
262 for x in 0..width {
263 let idx = y * width + x;
264 let color = fc(x, y);
265 let val = get(x as isize, y as isize);
266
267 match color {
268 0 => {
269 red[idx] = val;
271 cd_rg[idx] = val - green[idx];
272 }
273 2 => {
274 blue[idx] = val;
276 cd_bg[idx] = val - green[idx];
277 }
278 _ => {}
279 }
280 }
281 }
282
283 for y in 0..height {
287 for x in 0..width {
288 let idx = y * width + x;
289 let color = fc(x, y);
290 let ix = x as isize;
291 let iy = y as isize;
292
293 match color {
294 0 => {
295 let mut sum = 0.0f32;
297 let mut count = 0.0f32;
298 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
299 let nx = ix + dx as isize;
300 let ny = iy + dy as isize;
301 if nx >= 0 && nx < width as isize && ny >= 0 && ny < height as isize {
302 let nidx = ny as usize * width + nx as usize;
303 if fc(nx as usize, ny as usize) == 2 {
304 sum += cd_bg[nidx];
305 count += 1.0;
306 }
307 }
308 }
309 if count > 0.0 {
310 blue[idx] = green[idx] + sum / count;
311 } else {
312 blue[idx] = green[idx];
313 }
314 }
315 2 => {
316 let mut sum = 0.0f32;
318 let mut count = 0.0f32;
319 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
320 let nx = ix + dx as isize;
321 let ny = iy + dy as isize;
322 if nx >= 0 && nx < width as isize && ny >= 0 && ny < height as isize {
323 let nidx = ny as usize * width + nx as usize;
324 if fc(nx as usize, ny as usize) == 0 {
325 sum += cd_rg[nidx];
326 count += 1.0;
327 }
328 }
329 }
330 if count > 0.0 {
331 red[idx] = green[idx] + sum / count;
332 } else {
333 red[idx] = green[idx];
334 }
335 }
336 1 => {
337 let mut sum_r = 0.0f32;
339 let mut cnt_r = 0.0f32;
340 for &dx in &[-1i32, 1] {
341 let nx = ix + dx as isize;
342 if nx >= 0 && nx < width as isize {
343 let nidx = y * width + nx as usize;
344 if fc(nx as usize, y) == 0 {
345 sum_r += cd_rg[nidx];
346 cnt_r += 1.0;
347 }
348 }
349 }
350 red[idx] = green[idx] + if cnt_r > 0.0 { sum_r / cnt_r } else { 0.0 };
351
352 let mut sum_b = 0.0f32;
353 let mut cnt_b = 0.0f32;
354 for &dy in &[-1i32, 1] {
355 let ny = iy + dy as isize;
356 if ny >= 0 && ny < height as isize {
357 let nidx = ny as usize * width + x;
358 if fc(x, ny as usize) == 2 {
359 sum_b += cd_bg[nidx];
360 cnt_b += 1.0;
361 }
362 }
363 }
364 blue[idx] = green[idx] + if cnt_b > 0.0 { sum_b / cnt_b } else { 0.0 };
365 }
366 3 => {
367 let mut sum_b = 0.0f32;
369 let mut cnt_b = 0.0f32;
370 for &dx in &[-1i32, 1] {
371 let nx = ix + dx as isize;
372 if nx >= 0 && nx < width as isize {
373 let nidx = y * width + nx as usize;
374 if fc(nx as usize, y) == 2 {
375 sum_b += cd_bg[nidx];
376 cnt_b += 1.0;
377 }
378 }
379 }
380 blue[idx] = green[idx] + if cnt_b > 0.0 { sum_b / cnt_b } else { 0.0 };
381
382 let mut sum_r = 0.0f32;
383 let mut cnt_r = 0.0f32;
384 for &dy in &[-1i32, 1] {
385 let ny = iy + dy as isize;
386 if ny >= 0 && ny < height as isize {
387 let nidx = ny as usize * width + x;
388 if fc(x, ny as usize) == 0 {
389 sum_r += cd_rg[nidx];
390 cnt_r += 1.0;
391 }
392 }
393 }
394 red[idx] = green[idx] + if cnt_r > 0.0 { sum_r / cnt_r } else { 0.0 };
395 }
396 _ => unreachable!(),
397 }
398 }
399 }
400
401 let mut red_out = red.clone();
406 let mut blue_out = blue.clone();
407
408 for y in 2..height.saturating_sub(2) {
409 for x in 2..width.saturating_sub(2) {
410 let idx = y * width + x;
411 let g = green[idx];
412
413 let cd_r = red[idx] - g;
415 let cd_r_h1 = red[idx.wrapping_sub(1)] - green[idx.wrapping_sub(1)];
416 let cd_r_h2 = red[idx + 1] - green[idx + 1];
417 let cd_r_v1 = red[(y - 1) * width + x] - green[(y - 1) * width + x];
418 let cd_r_v2 = red[(y + 1) * width + x] - green[(y + 1) * width + x];
419
420 let avg_cd_r = (cd_r_h1 + cd_r_h2 + cd_r_v1 + cd_r_v2) * 0.25;
422 let var_r = (cd_r_h1 - avg_cd_r).abs()
423 + (cd_r_h2 - avg_cd_r).abs()
424 + (cd_r_v1 - avg_cd_r).abs()
425 + (cd_r_v2 - avg_cd_r).abs();
426
427 if (cd_r - avg_cd_r).abs() > var_r * 1.5 + 1.0 {
428 red_out[idx] = g + avg_cd_r;
429 }
430
431 let cd_b = blue[idx] - g;
433 let cd_b_h1 = blue[idx.wrapping_sub(1)] - green[idx.wrapping_sub(1)];
434 let cd_b_h2 = blue[idx + 1] - green[idx + 1];
435 let cd_b_v1 = blue[(y - 1) * width + x] - green[(y - 1) * width + x];
436 let cd_b_v2 = blue[(y + 1) * width + x] - green[(y + 1) * width + x];
437
438 let avg_cd_b = (cd_b_h1 + cd_b_h2 + cd_b_v1 + cd_b_v2) * 0.25;
439 let var_b = (cd_b_h1 - avg_cd_b).abs()
440 + (cd_b_h2 - avg_cd_b).abs()
441 + (cd_b_v1 - avg_cd_b).abs()
442 + (cd_b_v2 - avg_cd_b).abs();
443
444 if (cd_b - avg_cd_b).abs() > var_b * 1.5 + 1.0 {
445 blue_out[idx] = g + avg_cd_b;
446 }
447 }
448 }
449
450 output
454 .par_chunks_mut(width * 3)
455 .enumerate()
456 .for_each(|(y, row)| {
457 for x in 0..width {
458 let idx = y * width + x;
459 let out_idx = x * 3;
460 row[out_idx] = red_out[idx].round().clamp(0.0, white) as u16;
461 row[out_idx + 1] = green[idx].round().clamp(0.0, white) as u16;
462 row[out_idx + 2] = blue_out[idx].round().clamp(0.0, white) as u16;
463 }
464 });
465
466 Ok(())
467 }
468}
469
470pub struct Lmmse;
479
480impl Demosaic for Lmmse {
481 fn demosaic_into(&self, raw: &RawImage, output: &mut [u16]) -> Result<(), DemosaicError> {
482 let width = raw.active_area().size.width as usize;
483 let height = raw.active_area().size.height as usize;
484 let x_off = raw.active_area().origin.x as usize;
485 let y_off = raw.active_area().origin.y as usize;
486 let raw_w = raw.width() as usize;
487
488 let expected_size = width * height * 3;
489 if output.len() != expected_size {
490 return Err(DemosaicError::BufferSizeMismatch {
491 expected: expected_size,
492 actual: output.len(),
493 });
494 }
495
496 if width < 6 || height < 6 {
497 return Err(DemosaicError::InvalidDimensions);
498 }
499
500 let white = raw.white_level() as f32;
501
502 let fc = |x: usize, y: usize| -> u8 {
504 let ax = x + x_off;
505 let ay = y + y_off;
506 match raw.cfa_pattern() {
507 CfaPattern::Rggb => match (ax % 2, ay % 2) {
508 (0, 0) => 0,
509 (1, 0) => 1,
510 (0, 1) => 3,
511 _ => 2,
512 },
513 CfaPattern::Grbg => match (ax % 2, ay % 2) {
514 (0, 0) => 1,
515 (1, 0) => 0,
516 (0, 1) => 2,
517 _ => 3,
518 },
519 CfaPattern::Gbrg => match (ax % 2, ay % 2) {
520 (0, 0) => 3,
521 (1, 0) => 2,
522 (0, 1) => 0,
523 _ => 1,
524 },
525 CfaPattern::Bggr => match (ax % 2, ay % 2) {
526 (0, 0) => 2,
527 (1, 0) => 3,
528 (0, 1) => 1,
529 _ => 0,
530 },
531 }
532 };
533
534 let get = |x: isize, y: isize| -> f32 {
536 let cx = x.clamp(0, (width as isize) - 1) as usize;
537 let cy = y.clamp(0, (height as isize) - 1) as usize;
538 raw.data[(cy + y_off) * raw_w + (cx + x_off)] as f32
539 };
540
541 let mut gh = vec![0.0f32; width * height];
544 let mut gv = vec![0.0f32; width * height];
545
546 for y in 0..height {
547 for x in 0..width {
548 let color = fc(x, y);
549 let ix = x as isize;
550 let iy = y as isize;
551
552 if color == 1 || color == 3 {
553 let val = get(ix, iy);
555 gh[y * width + x] = val;
556 gv[y * width + x] = val;
557 } else {
558 let est_h = 0.5 * (get(ix - 1, iy) + get(ix + 1, iy))
561 + 0.25 * (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy));
562 gh[y * width + x] = est_h.clamp(0.0, white);
563
564 let est_v = 0.5 * (get(ix, iy - 1) + get(ix, iy + 1))
566 + 0.25 * (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2));
567 gv[y * width + x] = est_v.clamp(0.0, white);
568 }
569 }
570 }
571
572 let mut green = vec![0.0f32; width * height];
575 let eps = 1e-5f32;
576 let half_win = 2usize; for y in 0..height {
579 for x in 0..width {
580 let color = fc(x, y);
581 if color == 1 || color == 3 {
582 green[y * width + x] = gh[y * width + x]; continue;
584 }
585
586 let mut sum_h = 0.0f32;
588 let mut sum_sq_h = 0.0f32;
589 let mut sum_v = 0.0f32;
590 let mut sum_sq_v = 0.0f32;
591 let mut n = 0.0f32;
592
593 for dy in -(half_win as isize)..=(half_win as isize) {
594 for dx in -(half_win as isize)..=(half_win as isize) {
595 let nx = (x as isize + dx).clamp(0, (width as isize) - 1) as usize;
596 let ny = (y as isize + dy).clamp(0, (height as isize) - 1) as usize;
597 let nidx = ny * width + nx;
598 let raw_val = get(nx as isize, ny as isize);
599 let dh = raw_val - gh[nidx];
600 let dv = raw_val - gv[nidx];
601 sum_h += dh;
602 sum_sq_h += dh * dh;
603 sum_v += dv;
604 sum_sq_v += dv * dv;
605 n += 1.0;
606 }
607 }
608
609 let var_h = (sum_sq_h - sum_h * sum_h / n) / n;
610 let var_v = (sum_sq_v - sum_v * sum_v / n) / n;
611
612 let wh = 1.0 / (var_h + eps);
614 let wv = 1.0 / (var_v + eps);
615
616 let idx = y * width + x;
617 green[idx] = ((wh * gh[idx] + wv * gv[idx]) / (wh + wv)).clamp(0.0, white);
618 }
619 }
620
621 drop(gh);
622 drop(gv);
623
624 let mut cd_rg = vec![0.0f32; width * height]; let mut cd_bg = vec![0.0f32; width * height]; for y in 0..height {
631 for x in 0..width {
632 let idx = y * width + x;
633 match fc(x, y) {
634 0 => cd_rg[idx] = get(x as isize, y as isize) - green[idx],
635 2 => cd_bg[idx] = get(x as isize, y as isize) - green[idx],
636 _ => {}
637 }
638 }
639 }
640
641 let mut red = vec![0.0f32; width * height];
642 let mut blue = vec![0.0f32; width * height];
643
644 for y in 0..height {
645 for x in 0..width {
646 let idx = y * width + x;
647 let color = fc(x, y);
648 let ix = x as isize;
649 let iy = y as isize;
650
651 match color {
652 0 => {
653 red[idx] = get(ix, iy);
655 let mut sum = 0.0f32;
656 let mut cnt = 0.0f32;
657 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
658 let nx = ix + dx as isize;
659 let ny = iy + dy as isize;
660 if nx >= 0 && nx < width as isize && ny >= 0 && ny < height as isize {
661 let nidx = ny as usize * width + nx as usize;
662 if fc(nx as usize, ny as usize) == 2 {
663 sum += cd_bg[nidx];
664 cnt += 1.0;
665 }
666 }
667 }
668 blue[idx] = (green[idx] + if cnt > 0.0 { sum / cnt } else { 0.0 })
669 .clamp(0.0, white);
670 }
671 2 => {
672 blue[idx] = get(ix, iy);
674 let mut sum = 0.0f32;
675 let mut cnt = 0.0f32;
676 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
677 let nx = ix + dx as isize;
678 let ny = iy + dy as isize;
679 if nx >= 0 && nx < width as isize && ny >= 0 && ny < height as isize {
680 let nidx = ny as usize * width + nx as usize;
681 if fc(nx as usize, ny as usize) == 0 {
682 sum += cd_rg[nidx];
683 cnt += 1.0;
684 }
685 }
686 }
687 red[idx] = (green[idx] + if cnt > 0.0 { sum / cnt } else { 0.0 })
688 .clamp(0.0, white);
689 }
690 1 => {
691 let mut sr = 0.0f32;
693 let mut cr = 0.0f32;
694 for &dx in &[-1i32, 1] {
695 let nx = ix + dx as isize;
696 if nx >= 0 && nx < width as isize {
697 let nidx = y * width + nx as usize;
698 if fc(nx as usize, y) == 0 {
699 sr += cd_rg[nidx];
700 cr += 1.0;
701 }
702 }
703 }
704 red[idx] =
705 (green[idx] + if cr > 0.0 { sr / cr } else { 0.0 }).clamp(0.0, white);
706
707 let mut sb = 0.0f32;
708 let mut cb = 0.0f32;
709 for &dy in &[-1i32, 1] {
710 let ny = iy + dy as isize;
711 if ny >= 0 && ny < height as isize {
712 let nidx = ny as usize * width + x;
713 if fc(x, ny as usize) == 2 {
714 sb += cd_bg[nidx];
715 cb += 1.0;
716 }
717 }
718 }
719 blue[idx] =
720 (green[idx] + if cb > 0.0 { sb / cb } else { 0.0 }).clamp(0.0, white);
721 }
722 3 => {
723 let mut sb = 0.0f32;
725 let mut cb = 0.0f32;
726 for &dx in &[-1i32, 1] {
727 let nx = ix + dx as isize;
728 if nx >= 0 && nx < width as isize {
729 let nidx = y * width + nx as usize;
730 if fc(nx as usize, y) == 2 {
731 sb += cd_bg[nidx];
732 cb += 1.0;
733 }
734 }
735 }
736 blue[idx] =
737 (green[idx] + if cb > 0.0 { sb / cb } else { 0.0 }).clamp(0.0, white);
738
739 let mut sr = 0.0f32;
740 let mut cr = 0.0f32;
741 for &dy in &[-1i32, 1] {
742 let ny = iy + dy as isize;
743 if ny >= 0 && ny < height as isize {
744 let nidx = ny as usize * width + x;
745 if fc(x, ny as usize) == 0 {
746 sr += cd_rg[nidx];
747 cr += 1.0;
748 }
749 }
750 }
751 red[idx] =
752 (green[idx] + if cr > 0.0 { sr / cr } else { 0.0 }).clamp(0.0, white);
753 }
754 _ => unreachable!(),
755 }
756 }
757 }
758
759 output
762 .par_chunks_mut(width * 3)
763 .enumerate()
764 .for_each(|(y, row)| {
765 for x in 0..width {
766 let idx = y * width + x;
767 let out = x * 3;
768 row[out] = red[idx].round().clamp(0.0, white) as u16;
769 row[out + 1] = green[idx].round().clamp(0.0, white) as u16;
770 row[out + 2] = blue[idx].round().clamp(0.0, white) as u16;
771 }
772 });
773
774 Ok(())
775 }
776}
777
778pub struct Rcd;
787
788impl Demosaic for Rcd {
789 fn demosaic_into(&self, raw: &RawImage, output: &mut [u16]) -> Result<(), DemosaicError> {
790 let width = raw.active_area().size.width as usize;
791 let height = raw.active_area().size.height as usize;
792 let x_off = raw.active_area().origin.x as usize;
793 let y_off = raw.active_area().origin.y as usize;
794 let raw_w = raw.width() as usize;
795
796 let expected_size = width * height * 3;
797 if output.len() != expected_size {
798 return Err(DemosaicError::BufferSizeMismatch {
799 expected: expected_size,
800 actual: output.len(),
801 });
802 }
803
804 if width < 6 || height < 6 {
805 return Err(DemosaicError::InvalidDimensions);
806 }
807
808 let white = raw.white_level() as f32;
809
810 let fc = |x: usize, y: usize| -> u8 {
812 let ax = x + x_off;
813 let ay = y + y_off;
814 match raw.cfa_pattern() {
815 CfaPattern::Rggb => match (ax % 2, ay % 2) {
816 (0, 0) => 0,
817 (1, 0) => 1,
818 (0, 1) => 3,
819 _ => 2,
820 },
821 CfaPattern::Grbg => match (ax % 2, ay % 2) {
822 (0, 0) => 1,
823 (1, 0) => 0,
824 (0, 1) => 2,
825 _ => 3,
826 },
827 CfaPattern::Gbrg => match (ax % 2, ay % 2) {
828 (0, 0) => 3,
829 (1, 0) => 2,
830 (0, 1) => 0,
831 _ => 1,
832 },
833 CfaPattern::Bggr => match (ax % 2, ay % 2) {
834 (0, 0) => 2,
835 (1, 0) => 3,
836 (0, 1) => 1,
837 _ => 0,
838 },
839 }
840 };
841
842 let get = |x: isize, y: isize| -> f32 {
844 let cx = x.clamp(0, (width as isize) - 1) as usize;
845 let cy = y.clamp(0, (height as isize) - 1) as usize;
846 raw.data[(cy + y_off) * raw_w + (cx + x_off)] as f32
847 };
848
849 let mut green = vec![0.0f32; width * height];
852
853 for y in 0..height {
854 for x in 0..width {
855 let color = fc(x, y);
856 let ix = x as isize;
857 let iy = y as isize;
858
859 if color == 1 || color == 3 {
860 green[y * width + x] = get(ix, iy);
861 } else {
862 let dh = (get(ix - 1, iy) - get(ix + 1, iy)).abs()
864 + (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy)).abs();
865 let dv = (get(ix, iy - 1) - get(ix, iy + 1)).abs()
866 + (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2)).abs();
867
868 let gh = (get(ix - 1, iy) + get(ix + 1, iy)) * 0.5
869 + (2.0 * get(ix, iy) - get(ix - 2, iy) - get(ix + 2, iy)) * 0.25;
870 let gv = (get(ix, iy - 1) + get(ix, iy + 1)) * 0.5
871 + (2.0 * get(ix, iy) - get(ix, iy - 2) - get(ix, iy + 2)) * 0.25;
872
873 let eps = 1e-5f32;
874 let g = if dh < dv * 0.5 {
875 gh
876 } else if dv < dh * 0.5 {
877 gv
878 } else {
879 let wh = 1.0 / (dh + eps);
880 let wv = 1.0 / (dv + eps);
881 (wh * gh + wv * gv) / (wh + wv)
882 };
883 green[y * width + x] = g.max(0.0);
884 }
885 }
886 }
887
888 let mut ratio_rg = vec![1.0f32; width * height];
897 let mut ratio_bg = vec![1.0f32; width * height];
898
899 for y in 0..height {
900 for x in 0..width {
901 let idx = y * width + x;
902 let g = green[idx].max(1.0); match fc(x, y) {
904 0 => ratio_rg[idx] = get(x as isize, y as isize) / g,
905 2 => ratio_bg[idx] = get(x as isize, y as isize) / g,
906 _ => {}
907 }
908 }
909 }
910
911 let mut red = vec![0.0f32; width * height];
912 let mut blue = vec![0.0f32; width * height];
913
914 for y in 0..height {
915 for x in 0..width {
916 let idx = y * width + x;
917 let color = fc(x, y);
918 let ix = x as isize;
919 let iy = y as isize;
920
921 match color {
922 0 => {
923 red[idx] = get(ix, iy);
925 let mut sum_ratio = 0.0f32;
926 let mut cnt = 0.0f32;
927 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
928 let nx = ix + dx as isize;
929 let ny = iy + dy as isize;
930 if nx >= 0
931 && nx < width as isize
932 && ny >= 0
933 && ny < height as isize
934 && fc(nx as usize, ny as usize) == 2
935 {
936 sum_ratio += ratio_bg[ny as usize * width + nx as usize];
937 cnt += 1.0;
938 }
939 }
940 let r = if cnt > 0.0 { sum_ratio / cnt } else { 1.0 };
941 blue[idx] = (green[idx] * r).clamp(0.0, white);
942 }
943 2 => {
944 blue[idx] = get(ix, iy);
946 let mut sum_ratio = 0.0f32;
947 let mut cnt = 0.0f32;
948 for &(dx, dy) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
949 let nx = ix + dx as isize;
950 let ny = iy + dy as isize;
951 if nx >= 0
952 && nx < width as isize
953 && ny >= 0
954 && ny < height as isize
955 && fc(nx as usize, ny as usize) == 0
956 {
957 sum_ratio += ratio_rg[ny as usize * width + nx as usize];
958 cnt += 1.0;
959 }
960 }
961 let r = if cnt > 0.0 { sum_ratio / cnt } else { 1.0 };
962 red[idx] = (green[idx] * r).clamp(0.0, white);
963 }
964 1 => {
965 let mut sr = 0.0f32;
969 let mut cr = 0.0f32;
970 for &dx in &[-1i32, 1] {
971 let nx = ix + dx as isize;
972 if nx >= 0 && nx < width as isize && fc(nx as usize, y) == 0 {
973 sr += ratio_rg[y * width + nx as usize];
974 cr += 1.0;
975 }
976 }
977 let rr = if cr > 0.0 { sr / cr } else { 1.0 };
978 red[idx] = (green[idx] * rr).clamp(0.0, white);
979
980 let mut sb = 0.0f32;
981 let mut cb = 0.0f32;
982 for &dy in &[-1i32, 1] {
983 let ny = iy + dy as isize;
984 if ny >= 0 && ny < height as isize && fc(x, ny as usize) == 2 {
985 sb += ratio_bg[ny as usize * width + x];
986 cb += 1.0;
987 }
988 }
989 let rb = if cb > 0.0 { sb / cb } else { 1.0 };
990 blue[idx] = (green[idx] * rb).clamp(0.0, white);
991 }
992 3 => {
993 let mut sb = 0.0f32;
997 let mut cb = 0.0f32;
998 for &dx in &[-1i32, 1] {
999 let nx = ix + dx as isize;
1000 if nx >= 0 && nx < width as isize && fc(nx as usize, y) == 2 {
1001 sb += ratio_bg[y * width + nx as usize];
1002 cb += 1.0;
1003 }
1004 }
1005 let rb = if cb > 0.0 { sb / cb } else { 1.0 };
1006 blue[idx] = (green[idx] * rb).clamp(0.0, white);
1007
1008 let mut sr = 0.0f32;
1009 let mut cr = 0.0f32;
1010 for &dy in &[-1i32, 1] {
1011 let ny = iy + dy as isize;
1012 if ny >= 0 && ny < height as isize && fc(x, ny as usize) == 0 {
1013 sr += ratio_rg[ny as usize * width + x];
1014 cr += 1.0;
1015 }
1016 }
1017 let rr = if cr > 0.0 { sr / cr } else { 1.0 };
1018 red[idx] = (green[idx] * rr).clamp(0.0, white);
1019 }
1020 _ => unreachable!(),
1021 }
1022 }
1023 }
1024
1025 output
1028 .par_chunks_mut(width * 3)
1029 .enumerate()
1030 .for_each(|(y, row)| {
1031 for x in 0..width {
1032 let idx = y * width + x;
1033 let out = x * 3;
1034 row[out] = red[idx].round().clamp(0.0, white) as u16;
1035 row[out + 1] = green[idx].round().clamp(0.0, white) as u16;
1036 row[out + 2] = blue[idx].round().clamp(0.0, white) as u16;
1037 }
1038 });
1039
1040 Ok(())
1041 }
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046 use super::*;
1047 use crate::core::image::{Point, Rect, Size};
1048
1049 fn create_test_raw(width: u32, height: u32, pattern: CfaPattern, value: u16) -> RawImage {
1050 let size = Size::new(width, height);
1051 let active_area = Rect::new(Point::ORIGIN, size);
1052 RawImage::builder(size, active_area, 14, pattern)
1053 .white_level(16383)
1054 .data(vec![value; (width * height) as usize])
1055 .build()
1056 }
1057
1058 fn create_gradient_raw(width: u32, height: u32, pattern: CfaPattern) -> RawImage {
1059 let size = Size::new(width, height);
1060 let active_area = Rect::new(Point::ORIGIN, size);
1061 let mut data = vec![0u16; (width * height) as usize];
1062 for y in 0..height {
1063 for x in 0..width {
1064 let val =
1066 ((x as f32 / width as f32 + y as f32 / height as f32) * 0.5 * 8000.0) as u16;
1067 data[(y * width + x) as usize] = val;
1068 }
1069 }
1070 RawImage::builder(size, active_area, 14, pattern)
1071 .white_level(16383)
1072 .data(data)
1073 .build()
1074 }
1075
1076 #[test]
1077 fn test_amaze_correct_output_size() {
1078 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1079 let mut output = vec![0u16; 20 * 20 * 3];
1080 assert!(Amaze.demosaic_into(&raw, &mut output).is_ok());
1081 }
1082
1083 #[test]
1084 fn test_amaze_wrong_buffer_size() {
1085 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1086 let mut output = vec![0u16; 50];
1087 assert!(matches!(
1088 Amaze.demosaic_into(&raw, &mut output),
1089 Err(DemosaicError::BufferSizeMismatch { .. })
1090 ));
1091 }
1092
1093 #[test]
1094 fn test_amaze_too_small_image() {
1095 let raw = create_test_raw(4, 4, CfaPattern::Rggb, 5000);
1096 let mut output = vec![0u16; 4 * 4 * 3];
1097 assert!(matches!(
1098 Amaze.demosaic_into(&raw, &mut output),
1099 Err(DemosaicError::InvalidDimensions)
1100 ));
1101 }
1102
1103 #[test]
1104 fn test_amaze_uniform_produces_uniform() {
1105 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1106 let rgb = Amaze.demosaic(&raw);
1107
1108 for y in 4..16 {
1110 for x in 4..16 {
1111 let idx = (y * 20 + x) * 3;
1112 for c in 0..3 {
1113 let val = rgb.data[idx + c];
1114 assert!(
1115 (val as i32 - 5000).abs() < 500,
1116 "pixel ({},{}) ch {} = {}, expected ~5000",
1117 x,
1118 y,
1119 c,
1120 val
1121 );
1122 }
1123 }
1124 }
1125 }
1126
1127 #[test]
1128 fn test_amaze_all_cfa_patterns() {
1129 for pattern in [
1130 CfaPattern::Rggb,
1131 CfaPattern::Grbg,
1132 CfaPattern::Gbrg,
1133 CfaPattern::Bggr,
1134 ] {
1135 let raw = create_test_raw(20, 20, pattern, 3000);
1136 let rgb = Amaze.demosaic(&raw);
1137 assert_eq!(rgb.width(), 20);
1138 assert_eq!(rgb.height(), 20);
1139 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1140
1141 for val in &rgb.data {
1143 assert!(
1144 *val <= 16383,
1145 "pattern {:?}: value {} too high",
1146 pattern,
1147 val
1148 );
1149 }
1150 }
1151 }
1152
1153 #[test]
1154 fn test_amaze_gradient_smooth() {
1155 let raw = create_gradient_raw(40, 40, CfaPattern::Rggb);
1156 let rgb = Amaze.demosaic(&raw);
1157
1158 for y in 5..35 {
1160 for x in 5..35 {
1161 let idx = (y * 40 + x) * 3;
1162 let idx_right = (y * 40 + x + 1) * 3;
1163 let idx_down = ((y + 1) * 40 + x) * 3;
1164
1165 for c in 0..3 {
1166 let diff_h = (rgb.data[idx + c] as i32 - rgb.data[idx_right + c] as i32).abs();
1167 let diff_v = (rgb.data[idx + c] as i32 - rgb.data[idx_down + c] as i32).abs();
1168 assert!(
1169 diff_h < 1000,
1170 "horizontal jump at ({},{}) ch {}: {}",
1171 x,
1172 y,
1173 c,
1174 diff_h
1175 );
1176 assert!(
1177 diff_v < 1000,
1178 "vertical jump at ({},{}) ch {}: {}",
1179 x,
1180 y,
1181 c,
1182 diff_v
1183 );
1184 }
1185 }
1186 }
1187 }
1188
1189 #[test]
1190 fn test_amaze_preserves_known_green() {
1191 let mut raw = create_test_raw(10, 10, CfaPattern::Rggb, 0);
1193 raw.data[1] = 7000;
1195 let rgb = Amaze.demosaic(&raw);
1196 let g = rgb.data[3 + 1];
1199 assert_eq!(
1200 g, 7000,
1201 "green pixel should be preserved exactly, got {}",
1202 g
1203 );
1204 }
1205
1206 #[test]
1207 fn test_amaze_with_active_area() {
1208 let size = Size::new(30, 30);
1209 let active_area = Rect::from_coords(5, 5, 20, 20);
1210 let raw = RawImage::builder(size, active_area, 14, CfaPattern::Rggb)
1211 .white_level(16383)
1212 .data(vec![4000u16; 30 * 30])
1213 .build();
1214 let rgb = Amaze.demosaic(&raw);
1215 assert_eq!(rgb.width(), 20);
1216 assert_eq!(rgb.height(), 20);
1217 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1218 }
1219
1220 #[test]
1221 fn test_amaze_respects_white_level_clamp() {
1222 let white_level: u16 = 16383;
1224 let raw = create_test_raw(20, 20, CfaPattern::Rggb, white_level);
1225 let mut output = vec![0u16; 20 * 20 * 3];
1226 Amaze.demosaic_into(&raw, &mut output).unwrap();
1227 for (i, &v) in output.iter().enumerate() {
1228 assert!(
1229 v <= white_level,
1230 "pixel {} has value {} exceeding white_level {}",
1231 i,
1232 v,
1233 white_level
1234 );
1235 }
1236 }
1237
1238 #[test]
1241 fn test_lmmse_correct_output_size() {
1242 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1243 let mut output = vec![0u16; 20 * 20 * 3];
1244 assert!(Lmmse.demosaic_into(&raw, &mut output).is_ok());
1245 }
1246
1247 #[test]
1248 fn test_lmmse_wrong_buffer_size() {
1249 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1250 let mut output = vec![0u16; 50];
1251 assert!(matches!(
1252 Lmmse.demosaic_into(&raw, &mut output),
1253 Err(DemosaicError::BufferSizeMismatch { .. })
1254 ));
1255 }
1256
1257 #[test]
1258 fn test_lmmse_too_small_image() {
1259 let raw = create_test_raw(4, 4, CfaPattern::Rggb, 5000);
1260 let mut output = vec![0u16; 4 * 4 * 3];
1261 assert!(matches!(
1262 Lmmse.demosaic_into(&raw, &mut output),
1263 Err(DemosaicError::InvalidDimensions)
1264 ));
1265 }
1266
1267 #[test]
1268 fn test_lmmse_uniform_produces_uniform() {
1269 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1270 let rgb = Lmmse.demosaic(&raw);
1271
1272 for y in 4..16 {
1273 for x in 4..16 {
1274 let idx = (y * 20 + x) * 3;
1275 for c in 0..3 {
1276 let val = rgb.data[idx + c];
1277 assert!(
1278 (val as i32 - 5000).abs() < 500,
1279 "LMMSE pixel ({},{}) ch {} = {}, expected ~5000",
1280 x,
1281 y,
1282 c,
1283 val
1284 );
1285 }
1286 }
1287 }
1288 }
1289
1290 #[test]
1291 fn test_lmmse_all_cfa_patterns() {
1292 for pattern in [
1293 CfaPattern::Rggb,
1294 CfaPattern::Grbg,
1295 CfaPattern::Gbrg,
1296 CfaPattern::Bggr,
1297 ] {
1298 let raw = create_test_raw(20, 20, pattern, 3000);
1299 let rgb = Lmmse.demosaic(&raw);
1300 assert_eq!(rgb.width(), 20);
1301 assert_eq!(rgb.height(), 20);
1302 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1303
1304 for val in &rgb.data {
1305 assert!(
1306 *val <= 16383,
1307 "LMMSE pattern {:?}: value {} too high",
1308 pattern,
1309 val
1310 );
1311 }
1312 }
1313 }
1314
1315 #[test]
1316 fn test_lmmse_gradient_smooth() {
1317 let raw = create_gradient_raw(40, 40, CfaPattern::Rggb);
1318 let rgb = Lmmse.demosaic(&raw);
1319
1320 for y in 5..35 {
1321 for x in 5..35 {
1322 let idx = (y * 40 + x) * 3;
1323 let idx_right = (y * 40 + x + 1) * 3;
1324 let idx_down = ((y + 1) * 40 + x) * 3;
1325
1326 for c in 0..3 {
1327 let diff_h = (rgb.data[idx + c] as i32 - rgb.data[idx_right + c] as i32).abs();
1328 let diff_v = (rgb.data[idx + c] as i32 - rgb.data[idx_down + c] as i32).abs();
1329 assert!(
1330 diff_h < 1000,
1331 "LMMSE horizontal jump at ({},{}) ch {}: {}",
1332 x,
1333 y,
1334 c,
1335 diff_h
1336 );
1337 assert!(
1338 diff_v < 1000,
1339 "LMMSE vertical jump at ({},{}) ch {}: {}",
1340 x,
1341 y,
1342 c,
1343 diff_v
1344 );
1345 }
1346 }
1347 }
1348 }
1349
1350 #[test]
1351 fn test_lmmse_with_active_area() {
1352 let size = Size::new(30, 30);
1353 let active_area = Rect::from_coords(5, 5, 20, 20);
1354 let raw = RawImage::builder(size, active_area, 14, CfaPattern::Rggb)
1355 .white_level(16383)
1356 .data(vec![4000u16; 30 * 30])
1357 .build();
1358 let rgb = Lmmse.demosaic(&raw);
1359 assert_eq!(rgb.width(), 20);
1360 assert_eq!(rgb.height(), 20);
1361 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1362 }
1363
1364 #[test]
1367 fn test_rcd_correct_output_size() {
1368 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1369 let mut output = vec![0u16; 20 * 20 * 3];
1370 assert!(Rcd.demosaic_into(&raw, &mut output).is_ok());
1371 }
1372
1373 #[test]
1374 fn test_rcd_wrong_buffer_size() {
1375 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1376 let mut output = vec![0u16; 50];
1377 assert!(matches!(
1378 Rcd.demosaic_into(&raw, &mut output),
1379 Err(DemosaicError::BufferSizeMismatch { .. })
1380 ));
1381 }
1382
1383 #[test]
1384 fn test_rcd_too_small_image() {
1385 let raw = create_test_raw(4, 4, CfaPattern::Rggb, 5000);
1386 let mut output = vec![0u16; 4 * 4 * 3];
1387 assert!(matches!(
1388 Rcd.demosaic_into(&raw, &mut output),
1389 Err(DemosaicError::InvalidDimensions)
1390 ));
1391 }
1392
1393 #[test]
1394 fn test_rcd_uniform_produces_uniform() {
1395 let raw = create_test_raw(20, 20, CfaPattern::Rggb, 5000);
1396 let rgb = Rcd.demosaic(&raw);
1397
1398 for y in 4..16 {
1399 for x in 4..16 {
1400 let idx = (y * 20 + x) * 3;
1401 for c in 0..3 {
1402 let val = rgb.data[idx + c];
1403 assert!(
1404 (val as i32 - 5000).abs() < 500,
1405 "RCD pixel ({},{}) ch {} = {}, expected ~5000",
1406 x,
1407 y,
1408 c,
1409 val
1410 );
1411 }
1412 }
1413 }
1414 }
1415
1416 #[test]
1417 fn test_rcd_all_cfa_patterns() {
1418 for pattern in [
1419 CfaPattern::Rggb,
1420 CfaPattern::Grbg,
1421 CfaPattern::Gbrg,
1422 CfaPattern::Bggr,
1423 ] {
1424 let raw = create_test_raw(20, 20, pattern, 3000);
1425 let rgb = Rcd.demosaic(&raw);
1426 assert_eq!(rgb.width(), 20);
1427 assert_eq!(rgb.height(), 20);
1428 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1429
1430 for val in &rgb.data {
1431 assert!(
1432 *val <= 16383,
1433 "RCD pattern {:?}: value {} too high",
1434 pattern,
1435 val
1436 );
1437 }
1438 }
1439 }
1440
1441 #[test]
1442 fn test_rcd_gradient_smooth() {
1443 let raw = create_gradient_raw(40, 40, CfaPattern::Rggb);
1444 let rgb = Rcd.demosaic(&raw);
1445
1446 for y in 5..35 {
1447 for x in 5..35 {
1448 let idx = (y * 40 + x) * 3;
1449 let idx_right = (y * 40 + x + 1) * 3;
1450 let idx_down = ((y + 1) * 40 + x) * 3;
1451
1452 for c in 0..3 {
1453 let diff_h = (rgb.data[idx + c] as i32 - rgb.data[idx_right + c] as i32).abs();
1454 let diff_v = (rgb.data[idx + c] as i32 - rgb.data[idx_down + c] as i32).abs();
1455 assert!(
1456 diff_h < 1000,
1457 "RCD horizontal jump at ({},{}) ch {}: {}",
1458 x,
1459 y,
1460 c,
1461 diff_h
1462 );
1463 assert!(
1464 diff_v < 1000,
1465 "RCD vertical jump at ({},{}) ch {}: {}",
1466 x,
1467 y,
1468 c,
1469 diff_v
1470 );
1471 }
1472 }
1473 }
1474 }
1475
1476 #[test]
1477 fn test_rcd_with_active_area() {
1478 let size = Size::new(30, 30);
1479 let active_area = Rect::from_coords(5, 5, 20, 20);
1480 let raw = RawImage::builder(size, active_area, 14, CfaPattern::Rggb)
1481 .white_level(16383)
1482 .data(vec![4000u16; 30 * 30])
1483 .build();
1484 let rgb = Rcd.demosaic(&raw);
1485 assert_eq!(rgb.width(), 20);
1486 assert_eq!(rgb.height(), 20);
1487 assert_eq!(rgb.data.len(), 20 * 20 * 3);
1488 }
1489}