1use std::sync::Arc;
2
3#[cfg(feature = "parallel")]
4use crate::par_util;
5#[cfg(feature = "parallel")]
6use rayon::prelude::*;
7
8use ad_core_rs::color::{self, NDBayerPattern, NDColorMode};
9use ad_core_rs::ndarray::{NDArray, NDDataBuffer, NDDataType, NDDimension};
10use ad_core_rs::ndarray_pool::NDArrayPool;
11use ad_core_rs::plugin::runtime::{NDPluginProcess, ProcessResult};
12
13pub fn bayer_to_rgb1(src: &NDArray, pattern: NDBayerPattern) -> Option<NDArray> {
15 if src.dims.len() != 2 {
16 return None;
17 }
18 let w = src.dims[0].size;
19 let h = src.dims[1].size;
20
21 let offset_x = src.dims[0].offset;
23 let offset_y = src.dims[1].offset;
24
25 let n = w * h;
27 let src_vals: Vec<f64> = (0..n)
28 .map(|i| src.data.get_as_f64(i).unwrap_or(0.0))
29 .collect();
30 let get_val = |x: usize, y: usize| -> f64 { src_vals[y * w + x] };
31
32 let mut r = vec![0.0f64; n];
33 let mut g = vec![0.0f64; n];
34 let mut b = vec![0.0f64; n];
35
36 let (mut r_row_even, mut r_col_even) = match pattern {
38 NDBayerPattern::RGGB => (true, true),
39 NDBayerPattern::GBRG => (true, false),
40 NDBayerPattern::GRBG => (false, true),
41 NDBayerPattern::BGGR => (false, false),
42 };
43 if offset_x % 2 != 0 {
44 r_col_even = !r_col_even;
45 }
46 if offset_y % 2 != 0 {
47 r_row_even = !r_row_even;
48 }
49
50 let demosaic_row = |y: usize, r_row: &mut [f64], g_row: &mut [f64], b_row: &mut [f64]| {
58 let even_row = (y % 2 == 0) == r_row_even;
59 for x in 0..w {
60 let val = get_val(x, y);
61 let even_col = (x % 2 == 0) == r_col_even;
62 let interior = x > 0 && x + 1 < w && y > 0 && y + 1 < h;
63
64 match (even_row, even_col) {
65 (true, true) => {
66 r_row[x] = val;
69 if interior {
70 g_row[x] = (get_val(x - 1, y)
71 + get_val(x + 1, y)
72 + get_val(x, y - 1)
73 + get_val(x, y + 1))
74 / 4.0;
75 b_row[x] = (get_val(x - 1, y - 1)
76 + get_val(x + 1, y - 1)
77 + get_val(x - 1, y + 1)
78 + get_val(x + 1, y + 1))
79 / 4.0;
80 }
81 }
82 (true, false) | (false, true) => {
83 g_row[x] = val;
85 if interior {
86 if even_row {
87 r_row[x] = (get_val(x - 1, y) + get_val(x + 1, y)) / 2.0;
90 b_row[x] = (get_val(x, y - 1) + get_val(x, y + 1)) / 2.0;
91 } else {
92 b_row[x] = (get_val(x - 1, y) + get_val(x + 1, y)) / 2.0;
95 r_row[x] = (get_val(x, y - 1) + get_val(x, y + 1)) / 2.0;
96 }
97 }
98 }
99 (false, false) => {
100 b_row[x] = val;
103 if interior {
104 g_row[x] = (get_val(x - 1, y)
105 + get_val(x + 1, y)
106 + get_val(x, y - 1)
107 + get_val(x, y + 1))
108 / 4.0;
109 r_row[x] = (get_val(x - 1, y - 1)
110 + get_val(x + 1, y - 1)
111 + get_val(x - 1, y + 1)
112 + get_val(x + 1, y + 1))
113 / 4.0;
114 }
115 }
116 }
117 }
118 };
119
120 #[cfg(feature = "parallel")]
121 let use_parallel = par_util::should_parallelize(n);
122 #[cfg(not(feature = "parallel"))]
123 let use_parallel = false;
124
125 if use_parallel {
126 #[cfg(feature = "parallel")]
127 {
128 let r_rows: Vec<&mut [f64]> = r.chunks_mut(w).collect();
130 let g_rows: Vec<&mut [f64]> = g.chunks_mut(w).collect();
131 let b_rows: Vec<&mut [f64]> = b.chunks_mut(w).collect();
132
133 par_util::thread_pool().install(|| {
134 r_rows
135 .into_par_iter()
136 .zip(g_rows.into_par_iter())
137 .zip(b_rows.into_par_iter())
138 .enumerate()
139 .for_each(|(y, ((r_row, g_row), b_row))| {
140 demosaic_row(y, r_row, g_row, b_row);
141 });
142 });
143 }
144 } else {
145 for y in 0..h {
146 let row_start = y * w;
147 let row_end = row_start + w;
148 demosaic_row(
149 y,
150 &mut r[row_start..row_end],
151 &mut g[row_start..row_end],
152 &mut b[row_start..row_end],
153 );
154 }
155 }
156
157 let out_data = match src.data.data_type() {
159 NDDataType::UInt8 => {
160 let mut out = vec![0u8; n * 3];
161 for i in 0..n {
162 out[i * 3] = r[i].clamp(0.0, 255.0) as u8;
163 out[i * 3 + 1] = g[i].clamp(0.0, 255.0) as u8;
164 out[i * 3 + 2] = b[i].clamp(0.0, 255.0) as u8;
165 }
166 NDDataBuffer::U8(out)
167 }
168 NDDataType::UInt16 => {
169 let mut out = vec![0u16; n * 3];
170 for i in 0..n {
171 out[i * 3] = r[i].clamp(0.0, 65535.0) as u16;
172 out[i * 3 + 1] = g[i].clamp(0.0, 65535.0) as u16;
173 out[i * 3 + 2] = b[i].clamp(0.0, 65535.0) as u16;
174 }
175 NDDataBuffer::U16(out)
176 }
177 _ => return None,
178 };
179
180 let dims = vec![
181 NDDimension::new(3),
182 NDDimension::new(w),
183 NDDimension::new(h),
184 ];
185 let mut arr = NDArray::new(dims, src.data.data_type());
186 arr.data = out_data;
187 arr.unique_id = src.unique_id;
188 arr.timestamp = src.timestamp;
189 arr.attributes = src.attributes.clone();
190 Some(arr)
191}
192
193const RAINBOW_COLOR_MAP: [[u8; 3]; 256] = [
199 [0, 0, 0],
200 [0, 0, 4],
201 [0, 0, 8],
202 [0, 0, 12],
203 [0, 0, 16],
204 [0, 0, 20],
205 [0, 0, 24],
206 [0, 0, 28],
207 [0, 0, 32],
208 [0, 0, 36],
209 [0, 0, 40],
210 [0, 0, 45],
211 [0, 0, 49],
212 [0, 0, 53],
213 [0, 0, 57],
214 [0, 0, 61],
215 [0, 0, 65],
216 [0, 0, 69],
217 [0, 0, 73],
218 [0, 0, 77],
219 [0, 0, 81],
220 [0, 0, 85],
221 [0, 0, 89],
222 [0, 0, 93],
223 [0, 0, 97],
224 [0, 0, 101],
225 [0, 0, 105],
226 [0, 0, 109],
227 [0, 0, 113],
228 [0, 0, 117],
229 [0, 0, 121],
230 [0, 0, 125],
231 [0, 0, 130],
232 [0, 0, 134],
233 [0, 0, 138],
234 [0, 0, 142],
235 [0, 0, 146],
236 [0, 0, 150],
237 [0, 0, 154],
238 [0, 0, 158],
239 [0, 0, 162],
240 [0, 0, 166],
241 [0, 0, 170],
242 [0, 0, 174],
243 [0, 0, 178],
244 [0, 0, 182],
245 [0, 0, 186],
246 [0, 0, 190],
247 [0, 0, 194],
248 [0, 0, 198],
249 [0, 0, 202],
250 [0, 0, 206],
251 [0, 0, 210],
252 [0, 0, 215],
253 [0, 0, 219],
254 [0, 0, 223],
255 [0, 0, 227],
256 [0, 0, 231],
257 [0, 0, 235],
258 [0, 0, 239],
259 [0, 0, 243],
260 [0, 0, 247],
261 [0, 0, 251],
262 [0, 0, 255],
263 [0, 4, 255],
264 [0, 8, 255],
265 [0, 12, 255],
266 [0, 16, 255],
267 [0, 20, 255],
268 [0, 24, 255],
269 [0, 28, 255],
270 [0, 32, 255],
271 [0, 36, 255],
272 [0, 40, 255],
273 [0, 44, 255],
274 [0, 48, 255],
275 [0, 52, 255],
276 [0, 56, 255],
277 [0, 60, 255],
278 [0, 64, 255],
279 [0, 68, 255],
280 [0, 72, 255],
281 [0, 76, 255],
282 [0, 80, 255],
283 [0, 84, 255],
284 [0, 88, 255],
285 [0, 92, 255],
286 [0, 96, 255],
287 [0, 100, 255],
288 [0, 104, 255],
289 [0, 108, 255],
290 [0, 112, 255],
291 [0, 116, 255],
292 [0, 120, 255],
293 [0, 124, 255],
294 [0, 128, 255],
295 [0, 131, 255],
296 [0, 135, 255],
297 [0, 139, 255],
298 [0, 143, 255],
299 [0, 147, 255],
300 [0, 151, 255],
301 [0, 155, 255],
302 [0, 159, 255],
303 [0, 163, 255],
304 [0, 167, 255],
305 [0, 171, 255],
306 [0, 175, 255],
307 [0, 179, 255],
308 [0, 183, 255],
309 [0, 187, 255],
310 [0, 191, 255],
311 [0, 195, 255],
312 [0, 199, 255],
313 [0, 203, 255],
314 [0, 207, 255],
315 [0, 211, 255],
316 [0, 215, 255],
317 [0, 219, 255],
318 [0, 223, 255],
319 [0, 227, 255],
320 [0, 231, 255],
321 [0, 235, 255],
322 [0, 239, 255],
323 [0, 243, 255],
324 [0, 247, 255],
325 [0, 251, 255],
326 [0, 255, 255],
327 [4, 255, 251],
328 [8, 255, 247],
329 [12, 255, 243],
330 [16, 255, 239],
331 [20, 255, 235],
332 [24, 255, 231],
333 [28, 255, 227],
334 [32, 255, 223],
335 [36, 255, 219],
336 [40, 255, 215],
337 [44, 255, 211],
338 [48, 255, 207],
339 [52, 255, 203],
340 [56, 255, 199],
341 [60, 255, 195],
342 [64, 255, 191],
343 [68, 255, 187],
344 [72, 255, 183],
345 [76, 255, 179],
346 [80, 255, 175],
347 [84, 255, 171],
348 [88, 255, 167],
349 [92, 255, 163],
350 [96, 255, 159],
351 [100, 255, 155],
352 [104, 255, 151],
353 [108, 255, 147],
354 [112, 255, 143],
355 [116, 255, 139],
356 [120, 255, 135],
357 [124, 255, 131],
358 [128, 255, 128],
359 [131, 255, 124],
360 [135, 255, 120],
361 [139, 255, 116],
362 [143, 255, 112],
363 [147, 255, 108],
364 [151, 255, 104],
365 [155, 255, 100],
366 [159, 255, 96],
367 [163, 255, 92],
368 [167, 255, 88],
369 [171, 255, 84],
370 [175, 255, 80],
371 [179, 255, 76],
372 [183, 255, 72],
373 [187, 255, 68],
374 [191, 255, 64],
375 [195, 255, 60],
376 [199, 255, 56],
377 [203, 255, 52],
378 [207, 255, 48],
379 [211, 255, 44],
380 [215, 255, 40],
381 [219, 255, 36],
382 [223, 255, 32],
383 [227, 255, 28],
384 [231, 255, 24],
385 [235, 255, 20],
386 [239, 255, 16],
387 [243, 255, 12],
388 [247, 255, 8],
389 [251, 255, 4],
390 [255, 255, 0],
391 [255, 251, 0],
392 [255, 247, 0],
393 [255, 243, 0],
394 [255, 239, 0],
395 [255, 235, 0],
396 [255, 231, 0],
397 [255, 227, 0],
398 [255, 223, 0],
399 [255, 219, 0],
400 [255, 215, 0],
401 [255, 211, 0],
402 [255, 207, 0],
403 [255, 203, 0],
404 [255, 199, 0],
405 [255, 195, 0],
406 [255, 191, 0],
407 [255, 187, 0],
408 [255, 183, 0],
409 [255, 179, 0],
410 [255, 175, 0],
411 [255, 171, 0],
412 [255, 167, 0],
413 [255, 163, 0],
414 [255, 159, 0],
415 [255, 155, 0],
416 [255, 151, 0],
417 [255, 147, 0],
418 [255, 143, 0],
419 [255, 139, 0],
420 [255, 135, 0],
421 [255, 131, 0],
422 [255, 128, 0],
423 [255, 124, 0],
424 [255, 120, 0],
425 [255, 116, 0],
426 [255, 112, 0],
427 [255, 108, 0],
428 [255, 104, 0],
429 [255, 100, 0],
430 [255, 96, 0],
431 [255, 92, 0],
432 [255, 88, 0],
433 [255, 84, 0],
434 [255, 80, 0],
435 [255, 76, 0],
436 [255, 72, 0],
437 [255, 68, 0],
438 [255, 64, 0],
439 [255, 60, 0],
440 [255, 56, 0],
441 [255, 52, 0],
442 [255, 48, 0],
443 [255, 44, 0],
444 [255, 40, 0],
445 [255, 36, 0],
446 [255, 32, 0],
447 [255, 28, 0],
448 [255, 24, 0],
449 [255, 20, 0],
450 [255, 16, 0],
451 [255, 12, 0],
452 [255, 8, 0],
453 [255, 4, 0],
454 [255, 255, 255],
455];
456
457const IRON_COLOR_MAP: [[u8; 3]; 256] = [
463 [0, 0, 0],
464 [1, 0, 1],
465 [3, 1, 3],
466 [4, 1, 5],
467 [6, 2, 6],
468 [7, 2, 8],
469 [9, 3, 10],
470 [10, 3, 11],
471 [12, 4, 13],
472 [13, 4, 15],
473 [15, 5, 17],
474 [16, 5, 18],
475 [18, 6, 20],
476 [19, 6, 22],
477 [21, 7, 23],
478 [22, 7, 25],
479 [24, 8, 27],
480 [25, 8, 28],
481 [27, 9, 30],
482 [28, 9, 32],
483 [30, 10, 34],
484 [31, 10, 35],
485 [33, 11, 37],
486 [34, 11, 39],
487 [36, 12, 40],
488 [37, 12, 42],
489 [39, 13, 44],
490 [40, 13, 45],
491 [42, 14, 47],
492 [43, 14, 49],
493 [45, 15, 51],
494 [46, 15, 52],
495 [48, 16, 54],
496 [49, 16, 56],
497 [51, 17, 57],
498 [52, 17, 59],
499 [54, 18, 61],
500 [55, 18, 62],
501 [57, 19, 64],
502 [58, 19, 66],
503 [60, 20, 68],
504 [61, 20, 69],
505 [63, 21, 71],
506 [64, 21, 73],
507 [66, 22, 74],
508 [67, 22, 76],
509 [69, 23, 78],
510 [70, 23, 79],
511 [72, 24, 81],
512 [73, 24, 83],
513 [75, 25, 85],
514 [76, 25, 86],
515 [78, 26, 88],
516 [79, 26, 90],
517 [81, 27, 91],
518 [82, 27, 93],
519 [84, 28, 95],
520 [85, 28, 96],
521 [87, 29, 98],
522 [88, 29, 100],
523 [90, 30, 102],
524 [91, 30, 103],
525 [93, 31, 105],
526 [94, 31, 107],
527 [96, 32, 108],
528 [97, 32, 110],
529 [99, 33, 112],
530 [100, 33, 113],
531 [102, 34, 115],
532 [103, 34, 117],
533 [105, 35, 119],
534 [106, 35, 120],
535 [108, 36, 120],
536 [109, 36, 119],
537 [111, 37, 118],
538 [112, 37, 117],
539 [114, 38, 116],
540 [115, 38, 115],
541 [117, 39, 114],
542 [118, 39, 113],
543 [120, 40, 112],
544 [121, 40, 111],
545 [123, 41, 110],
546 [124, 41, 109],
547 [126, 42, 108],
548 [127, 42, 107],
549 [129, 43, 106],
550 [130, 43, 105],
551 [132, 44, 104],
552 [133, 44, 103],
553 [135, 45, 102],
554 [136, 45, 101],
555 [138, 46, 100],
556 [139, 46, 99],
557 [141, 47, 98],
558 [142, 47, 97],
559 [144, 48, 96],
560 [145, 48, 95],
561 [147, 49, 94],
562 [148, 49, 93],
563 [150, 50, 92],
564 [151, 50, 91],
565 [153, 51, 90],
566 [154, 51, 89],
567 [156, 52, 88],
568 [157, 52, 87],
569 [159, 53, 86],
570 [160, 53, 85],
571 [162, 54, 84],
572 [163, 54, 83],
573 [165, 55, 82],
574 [166, 55, 81],
575 [168, 56, 80],
576 [169, 56, 79],
577 [171, 57, 78],
578 [172, 57, 77],
579 [174, 58, 76],
580 [175, 58, 75],
581 [177, 59, 74],
582 [178, 59, 73],
583 [180, 60, 72],
584 [181, 60, 71],
585 [183, 61, 70],
586 [184, 61, 69],
587 [186, 62, 68],
588 [187, 62, 67],
589 [189, 63, 66],
590 [190, 63, 65],
591 [192, 64, 64],
592 [192, 65, 64],
593 [192, 67, 64],
594 [192, 68, 64],
595 [192, 70, 64],
596 [192, 71, 64],
597 [192, 73, 64],
598 [192, 74, 64],
599 [192, 76, 64],
600 [192, 77, 64],
601 [192, 79, 64],
602 [192, 80, 64],
603 [192, 82, 64],
604 [192, 83, 64],
605 [192, 85, 64],
606 [192, 86, 64],
607 [192, 88, 64],
608 [192, 89, 64],
609 [192, 91, 64],
610 [192, 92, 64],
611 [192, 94, 64],
612 [192, 95, 64],
613 [192, 97, 64],
614 [192, 98, 64],
615 [192, 100, 64],
616 [192, 101, 64],
617 [192, 103, 64],
618 [192, 104, 64],
619 [192, 106, 64],
620 [192, 107, 64],
621 [192, 109, 64],
622 [192, 110, 64],
623 [192, 112, 64],
624 [192, 113, 64],
625 [192, 115, 64],
626 [192, 116, 64],
627 [192, 118, 64],
628 [192, 119, 64],
629 [192, 121, 64],
630 [192, 122, 64],
631 [192, 124, 64],
632 [192, 125, 64],
633 [192, 127, 64],
634 [192, 128, 64],
635 [192, 130, 64],
636 [192, 131, 64],
637 [192, 133, 64],
638 [192, 134, 64],
639 [192, 136, 64],
640 [192, 137, 64],
641 [192, 139, 64],
642 [192, 140, 64],
643 [192, 142, 64],
644 [192, 143, 64],
645 [192, 145, 64],
646 [192, 146, 64],
647 [192, 148, 64],
648 [192, 149, 64],
649 [192, 151, 64],
650 [192, 152, 64],
651 [192, 154, 64],
652 [192, 155, 64],
653 [192, 157, 64],
654 [192, 158, 64],
655 [192, 160, 64],
656 [192, 161, 64],
657 [192, 163, 64],
658 [192, 164, 64],
659 [192, 166, 64],
660 [192, 167, 64],
661 [192, 169, 64],
662 [192, 170, 64],
663 [192, 172, 64],
664 [192, 173, 64],
665 [192, 175, 64],
666 [192, 176, 64],
667 [192, 178, 64],
668 [192, 179, 64],
669 [192, 181, 64],
670 [192, 182, 64],
671 [192, 184, 64],
672 [192, 185, 64],
673 [192, 187, 64],
674 [192, 188, 64],
675 [192, 190, 64],
676 [192, 191, 64],
677 [192, 193, 64],
678 [192, 194, 64],
679 [192, 196, 64],
680 [192, 197, 64],
681 [192, 199, 64],
682 [192, 200, 64],
683 [192, 202, 64],
684 [192, 203, 64],
685 [192, 205, 64],
686 [192, 206, 64],
687 [192, 208, 64],
688 [192, 209, 64],
689 [192, 211, 64],
690 [192, 212, 64],
691 [192, 214, 64],
692 [192, 215, 64],
693 [192, 217, 64],
694 [192, 218, 64],
695 [192, 220, 64],
696 [192, 221, 64],
697 [192, 223, 64],
698 [192, 224, 64],
699 [192, 226, 64],
700 [192, 227, 64],
701 [192, 229, 64],
702 [192, 230, 64],
703 [192, 232, 64],
704 [192, 233, 64],
705 [192, 235, 64],
706 [192, 236, 64],
707 [192, 238, 64],
708 [192, 239, 64],
709 [192, 241, 64],
710 [192, 242, 64],
711 [192, 244, 64],
712 [192, 245, 64],
713 [192, 247, 64],
714 [192, 248, 64],
715 [192, 250, 64],
716 [192, 251, 64],
717 [192, 253, 64],
718 [255, 255, 255],
719];
720
721fn false_color_lut(false_color: i32) -> Option<&'static [[u8; 3]; 256]> {
726 match false_color {
727 1 => Some(&RAINBOW_COLOR_MAP),
728 2 => Some(&IRON_COLOR_MAP),
729 _ => None,
730 }
731}
732
733fn false_color_mono_to_rgb1(src: &NDArray, false_color: i32) -> Option<NDArray> {
741 if src.dims.len() != 2 || src.data.data_type() != NDDataType::UInt8 {
742 return None;
743 }
744 let lut = false_color_lut(false_color)?;
745
746 let w = src.dims[0].size;
747 let h = src.dims[1].size;
748 let n = w * h;
749
750 let src_slice = src.data.as_u8_slice();
751 let mut out = vec![0u8; n * 3];
752 for i in 0..n {
753 let val = src_slice[i] as usize;
754 let [r, g, b] = lut[val];
755 out[i * 3] = r;
756 out[i * 3 + 1] = g;
757 out[i * 3 + 2] = b;
758 }
759
760 let dims = vec![
761 NDDimension::new(3),
762 NDDimension::new(w),
763 NDDimension::new(h),
764 ];
765 let mut arr = NDArray::new(dims, NDDataType::UInt8);
766 arr.data = NDDataBuffer::U8(out);
767 arr.unique_id = src.unique_id;
768 arr.timestamp = src.timestamp;
769 arr.attributes = src.attributes.clone();
770 Some(arr)
771}
772
773fn detect_color_mode(array: &NDArray) -> NDColorMode {
779 if let Some(attr) = array.attributes.get("ColorMode") {
780 if let Some(v) = attr.value.as_i64() {
781 return NDColorMode::from_i32(v as i32);
782 }
783 }
784 match array.dims.len() {
785 0 | 1 => NDColorMode::Mono,
786 2 => NDColorMode::Mono,
787 3 => {
788 if array.dims[0].size == 3 {
789 NDColorMode::RGB1
790 } else if array.dims[1].size == 3 {
791 NDColorMode::RGB2
792 } else if array.dims[2].size == 3 {
793 NDColorMode::RGB3
794 } else {
795 NDColorMode::Mono
796 }
797 }
798 _ => NDColorMode::Mono,
799 }
800}
801
802#[derive(Debug, Clone)]
804pub struct ColorConvertConfig {
805 pub target_mode: NDColorMode,
806 pub bayer_pattern: NDBayerPattern,
807 pub false_color: i32,
809}
810
811pub struct ColorConvertProcessor {
813 config: ColorConvertConfig,
814 color_mode_out_idx: Option<usize>,
815 false_color_idx: Option<usize>,
816}
817
818impl ColorConvertProcessor {
819 pub fn new(config: ColorConvertConfig) -> Self {
820 Self {
821 config,
822 color_mode_out_idx: None,
823 false_color_idx: None,
824 }
825 }
826}
827
828impl NDPluginProcess for ColorConvertProcessor {
829 fn register_params(
830 &mut self,
831 base: &mut asyn_rs::port::PortDriverBase,
832 ) -> asyn_rs::error::AsynResult<()> {
833 use asyn_rs::param::ParamType;
834 base.create_param("COLOR_MODE_OUT", ParamType::Int32)?;
835 base.create_param("FALSE_COLOR", ParamType::Int32)?;
836 self.color_mode_out_idx = base.find_param("COLOR_MODE_OUT");
837 self.false_color_idx = base.find_param("FALSE_COLOR");
838 Ok(())
839 }
840
841 fn on_param_change(
842 &mut self,
843 reason: usize,
844 params: &ad_core_rs::plugin::runtime::PluginParamSnapshot,
845 ) -> ad_core_rs::plugin::runtime::ParamChangeResult {
846 if Some(reason) == self.color_mode_out_idx {
847 self.config.target_mode = NDColorMode::from_i32(params.value.as_i32());
848 } else if Some(reason) == self.false_color_idx {
849 self.config.false_color = params.value.as_i32();
850 }
851 ad_core_rs::plugin::runtime::ParamChangeResult::updates(vec![])
852 }
853
854 fn process_array(&mut self, array: &NDArray, _pool: &NDArrayPool) -> ProcessResult {
855 let src_mode = detect_color_mode(array);
856 let target = self.config.target_mode;
857
858 if src_mode == target {
860 return ProcessResult::arrays(vec![Arc::new(array.clone())]);
861 }
862
863 let rgb1 = match src_mode {
865 NDColorMode::RGB1 => Some(array.clone()),
866 NDColorMode::Mono => {
867 if self.config.false_color != 0 {
868 false_color_mono_to_rgb1(array, self.config.false_color)
869 .or_else(|| color::mono_to_rgb1(array).ok())
870 } else {
871 color::mono_to_rgb1(array).ok()
872 }
873 }
874 NDColorMode::Bayer => bayer_to_rgb1(array, self.config.bayer_pattern),
875 NDColorMode::RGB2 | NDColorMode::RGB3 => {
876 color::convert_rgb_layout(array, src_mode, NDColorMode::RGB1).ok()
877 }
878 NDColorMode::YUV444 => color::yuv444_to_rgb1(array).ok(),
879 NDColorMode::YUV422 => color::yuv422_to_rgb1(array).ok(),
880 NDColorMode::YUV411 => color::yuv411_to_rgb1(array).ok(),
881 };
882
883 let rgb1 = match rgb1 {
884 Some(r) => r,
885 None => return ProcessResult::empty(),
886 };
887
888 let result = match target {
890 NDColorMode::RGB1 => Some(rgb1),
891 NDColorMode::Mono => color::rgb1_to_mono(&rgb1).ok(),
892 NDColorMode::Bayer => None,
893 NDColorMode::RGB2 | NDColorMode::RGB3 => {
894 color::convert_rgb_layout(&rgb1, NDColorMode::RGB1, target).ok()
895 }
896 NDColorMode::YUV444 => color::rgb1_to_yuv444(&rgb1).ok(),
897 NDColorMode::YUV422 => color::rgb1_to_yuv422(&rgb1).ok(),
898 NDColorMode::YUV411 => color::rgb1_to_yuv411(&rgb1).ok(),
899 };
900
901 match result {
902 Some(mut out) => {
903 let color_mode_val = match target {
905 NDColorMode::Mono => 0i32,
906 NDColorMode::Bayer => 1,
907 NDColorMode::RGB1 => 2,
908 NDColorMode::RGB2 => 3,
909 NDColorMode::RGB3 => 4,
910 NDColorMode::YUV444 => 5,
911 NDColorMode::YUV422 => 6,
912 NDColorMode::YUV411 => 7,
913 };
914 use ad_core_rs::attributes::{NDAttrSource, NDAttrValue, NDAttribute};
915 out.attributes.add(NDAttribute::new_static(
916 "ColorMode",
917 "Color Mode",
918 NDAttrSource::Driver,
919 NDAttrValue::Int32(color_mode_val),
920 ));
921 ProcessResult::arrays(vec![Arc::new(out)])
922 }
923 None => ProcessResult::empty(),
924 }
925 }
926
927 fn plugin_type(&self) -> &str {
928 "NDPluginColorConvert"
929 }
930}
931
932#[cfg(test)]
933mod tests {
934 use super::*;
935
936 #[test]
937 fn test_bayer_to_rgb1_basic() {
938 let mut arr = NDArray::new(
940 vec![NDDimension::new(4), NDDimension::new(4)],
941 NDDataType::UInt8,
942 );
943 if let NDDataBuffer::U8(ref mut v) = arr.data {
944 for i in 0..16 {
946 v[i] = 128;
947 }
948 }
949
950 let rgb = bayer_to_rgb1(&arr, NDBayerPattern::RGGB).unwrap();
951 assert_eq!(rgb.dims.len(), 3);
952 assert_eq!(rgb.dims[0].size, 3); assert_eq!(rgb.dims[1].size, 4); assert_eq!(rgb.dims[2].size, 4); }
956
957 fn rgb1_pixel(arr: &NDArray, w: usize, x: usize, y: usize) -> [u8; 3] {
959 let i = (y * w + x) * 3;
960 if let NDDataBuffer::U8(ref v) = arr.data {
961 [v[i], v[i + 1], v[i + 2]]
962 } else {
963 panic!("expected UInt8 RGB1 output");
964 }
965 }
966
967 #[test]
968 fn test_adp4_bayer_border_keeps_native_channel_only() {
969 let mut arr = NDArray::new(
974 vec![NDDimension::new(4), NDDimension::new(4)],
975 NDDataType::UInt8,
976 );
977 if let NDDataBuffer::U8(ref mut v) = arr.data {
978 v.iter_mut().for_each(|p| *p = 100);
979 }
980 let rgb = bayer_to_rgb1(&arr, NDBayerPattern::RGGB).unwrap();
981
982 assert_eq!(rgb1_pixel(&rgb, 4, 0, 0), [100, 0, 0]); assert_eq!(rgb1_pixel(&rgb, 4, 1, 0), [0, 100, 0]); assert_eq!(rgb1_pixel(&rgb, 4, 0, 1), [0, 100, 0]); assert_eq!(rgb1_pixel(&rgb, 4, 3, 3), [0, 0, 100]); assert_eq!(rgb1_pixel(&rgb, 4, 1, 1), [100, 100, 100]); assert_eq!(rgb1_pixel(&rgb, 4, 1, 2), [100, 100, 100]); assert_eq!(rgb1_pixel(&rgb, 4, 2, 2), [100, 100, 100]); }
993
994 #[test]
995 fn test_adp4_bayer_interior_uses_fixed_quarter_divisor() {
996 let mut arr = NDArray::new(
1002 vec![NDDimension::new(3), NDDimension::new(3)],
1003 NDDataType::UInt8,
1004 );
1005 if let NDDataBuffer::U8(ref mut v) = arr.data {
1006 v.copy_from_slice(&[200, 40, 200, 80, 128, 80, 200, 40, 200]);
1008 }
1009 let rgb = bayer_to_rgb1(&arr, NDBayerPattern::RGGB).unwrap();
1010
1011 assert_eq!(rgb1_pixel(&rgb, 3, 1, 1), [200, 60, 128]);
1013 assert_eq!(rgb1_pixel(&rgb, 3, 0, 0), [200, 0, 0]); assert_eq!(rgb1_pixel(&rgb, 3, 1, 0), [0, 40, 0]); }
1017
1018 #[test]
1019 fn test_color_convert_processor_bayer() {
1020 let config = ColorConvertConfig {
1021 target_mode: NDColorMode::RGB1,
1022 bayer_pattern: NDBayerPattern::RGGB,
1023 false_color: 0,
1024 };
1025 let mut proc = ColorConvertProcessor::new(config);
1026 let pool = NDArrayPool::new(1_000_000);
1027
1028 let mut arr = NDArray::new(
1029 vec![NDDimension::new(4), NDDimension::new(4)],
1030 NDDataType::UInt8,
1031 );
1032 if let NDDataBuffer::U8(ref mut v) = arr.data {
1033 for i in 0..16 {
1034 v[i] = 128;
1035 }
1036 }
1037
1038 let result = proc.process_array(&arr, &pool);
1039 assert_eq!(result.output_arrays.len(), 1);
1040 assert_eq!(result.output_arrays[0].dims[0].size, 3); }
1042
1043 #[test]
1044 fn test_false_color_conversion() {
1045 let config = ColorConvertConfig {
1046 target_mode: NDColorMode::RGB1,
1047 bayer_pattern: NDBayerPattern::RGGB,
1048 false_color: 1,
1049 };
1050 let mut proc = ColorConvertProcessor::new(config);
1051 let pool = NDArrayPool::new(1_000_000);
1052
1053 let mut arr = NDArray::new(
1055 vec![NDDimension::new(4), NDDimension::new(4)],
1056 NDDataType::UInt8,
1057 );
1058 if let NDDataBuffer::U8(ref mut v) = arr.data {
1059 for i in 0..16 {
1060 v[i] = (i * 17) as u8; }
1062 }
1063
1064 let result = proc.process_array(&arr, &pool);
1065 assert_eq!(result.output_arrays.len(), 1);
1066 let out = &result.output_arrays[0];
1067 assert_eq!(out.dims.len(), 3);
1068 assert_eq!(out.dims[0].size, 3); assert_eq!(out.dims[1].size, 4); assert_eq!(out.dims[2].size, 4); if let NDDataBuffer::U8(ref v) = out.data {
1075 assert_eq!([v[0], v[1], v[2]], RAINBOW_COLOR_MAP[0]);
1076 assert_eq!([v[0], v[1], v[2]], [0, 0, 0]);
1077 let last = 15 * 3;
1079 assert_eq!([v[last], v[last + 1], v[last + 2]], RAINBOW_COLOR_MAP[255]);
1080 } else {
1081 panic!("expected UInt8 output");
1082 }
1083 }
1084
1085 #[test]
1086 fn test_false_color_iron_table() {
1087 let config = ColorConvertConfig {
1088 target_mode: NDColorMode::RGB1,
1089 bayer_pattern: NDBayerPattern::RGGB,
1090 false_color: 2,
1091 };
1092 let mut proc = ColorConvertProcessor::new(config);
1093 let pool = NDArrayPool::new(1_000_000);
1094
1095 let mut arr = NDArray::new(
1097 vec![NDDimension::new(2), NDDimension::new(1)],
1098 NDDataType::UInt8,
1099 );
1100 if let NDDataBuffer::U8(ref mut v) = arr.data {
1101 v[0] = 0;
1102 v[1] = 192;
1103 }
1104
1105 let result = proc.process_array(&arr, &pool);
1106 let out = &result.output_arrays[0];
1107 if let NDDataBuffer::U8(ref v) = out.data {
1108 assert_eq!([v[0], v[1], v[2]], IRON_COLOR_MAP[0]);
1110 assert_eq!([v[3], v[4], v[5]], IRON_COLOR_MAP[192]);
1111 assert_eq!([v[3], v[4], v[5]], [192, 160, 64]);
1113 assert_ne!(IRON_COLOR_MAP[192], RAINBOW_COLOR_MAP[192]);
1114 } else {
1115 panic!("expected UInt8 output");
1116 }
1117 }
1118
1119 #[test]
1120 fn test_rgb1_to_rgb2_conversion() {
1121 let config = ColorConvertConfig {
1122 target_mode: NDColorMode::RGB2,
1123 bayer_pattern: NDBayerPattern::RGGB,
1124 false_color: 0,
1125 };
1126 let mut proc = ColorConvertProcessor::new(config);
1127 let pool = NDArrayPool::new(1_000_000);
1128
1129 let mut arr = NDArray::new(
1131 vec![
1132 NDDimension::new(3),
1133 NDDimension::new(4),
1134 NDDimension::new(4),
1135 ],
1136 NDDataType::UInt8,
1137 );
1138 if let NDDataBuffer::U8(ref mut v) = arr.data {
1139 for i in 0..v.len() {
1140 v[i] = (i % 256) as u8;
1141 }
1142 }
1143
1144 let result = proc.process_array(&arr, &pool);
1145 assert_eq!(result.output_arrays.len(), 1);
1146 let out = &result.output_arrays[0];
1147 assert_eq!(out.dims.len(), 3);
1148 assert_eq!(out.dims[1].size, 3);
1150 }
1151
1152 #[test]
1153 fn test_rgb2_to_mono_conversion() {
1154 let config = ColorConvertConfig {
1155 target_mode: NDColorMode::Mono,
1156 bayer_pattern: NDBayerPattern::RGGB,
1157 false_color: 0,
1158 };
1159 let mut proc = ColorConvertProcessor::new(config);
1160 let pool = NDArrayPool::new(1_000_000);
1161
1162 let mut arr = NDArray::new(
1164 vec![
1165 NDDimension::new(4),
1166 NDDimension::new(3),
1167 NDDimension::new(4),
1168 ],
1169 NDDataType::UInt8,
1170 );
1171 if let NDDataBuffer::U8(ref mut v) = arr.data {
1172 for i in 0..v.len() {
1173 v[i] = 128;
1174 }
1175 }
1176
1177 let result = proc.process_array(&arr, &pool);
1178 assert_eq!(result.output_arrays.len(), 1);
1179 let out = &result.output_arrays[0];
1180 assert_eq!(out.dims.len(), 2);
1182 }
1183
1184 #[test]
1185 fn test_detect_color_mode() {
1186 let arr2d = NDArray::new(
1188 vec![NDDimension::new(4), NDDimension::new(4)],
1189 NDDataType::UInt8,
1190 );
1191 assert_eq!(detect_color_mode(&arr2d), NDColorMode::Mono);
1192
1193 let arr_rgb1 = NDArray::new(
1195 vec![
1196 NDDimension::new(3),
1197 NDDimension::new(4),
1198 NDDimension::new(4),
1199 ],
1200 NDDataType::UInt8,
1201 );
1202 assert_eq!(detect_color_mode(&arr_rgb1), NDColorMode::RGB1);
1203
1204 let arr_rgb2 = NDArray::new(
1206 vec![
1207 NDDimension::new(4),
1208 NDDimension::new(3),
1209 NDDimension::new(4),
1210 ],
1211 NDDataType::UInt8,
1212 );
1213 assert_eq!(detect_color_mode(&arr_rgb2), NDColorMode::RGB2);
1214
1215 let arr_rgb3 = NDArray::new(
1217 vec![
1218 NDDimension::new(4),
1219 NDDimension::new(4),
1220 NDDimension::new(3),
1221 ],
1222 NDDataType::UInt8,
1223 );
1224 assert_eq!(detect_color_mode(&arr_rgb3), NDColorMode::RGB3);
1225 }
1226
1227 #[test]
1228 fn test_same_mode_passthrough() {
1229 let config = ColorConvertConfig {
1230 target_mode: NDColorMode::Mono,
1231 bayer_pattern: NDBayerPattern::RGGB,
1232 false_color: 0,
1233 };
1234 let mut proc = ColorConvertProcessor::new(config);
1235 let pool = NDArrayPool::new(1_000_000);
1236
1237 let mut arr = NDArray::new(
1239 vec![NDDimension::new(4), NDDimension::new(4)],
1240 NDDataType::UInt8,
1241 );
1242 arr.unique_id = 42;
1243 if let NDDataBuffer::U8(ref mut v) = arr.data {
1244 for i in 0..16 {
1245 v[i] = i as u8;
1246 }
1247 }
1248
1249 let result = proc.process_array(&arr, &pool);
1250 assert_eq!(result.output_arrays.len(), 1);
1251 assert_eq!(result.output_arrays[0].unique_id, 42);
1252 assert_eq!(result.output_arrays[0].dims.len(), 2);
1253 }
1254
1255 fn set_color_mode_attr(arr: &mut NDArray, mode: NDColorMode) {
1256 use ad_core_rs::attributes::{NDAttrSource, NDAttrValue, NDAttribute};
1257 arr.attributes.add(NDAttribute::new_static(
1258 "ColorMode",
1259 String::new(),
1260 NDAttrSource::Driver,
1261 NDAttrValue::Int32(mode as i32),
1262 ));
1263 }
1264
1265 #[test]
1266 fn test_bayer_to_mono_via_rgb1() {
1267 let config = ColorConvertConfig {
1268 target_mode: NDColorMode::Mono,
1269 bayer_pattern: NDBayerPattern::RGGB,
1270 false_color: 0,
1271 };
1272 let mut proc = ColorConvertProcessor::new(config);
1273 let pool = NDArrayPool::new(1_000_000);
1274
1275 let mut arr = NDArray::new(
1276 vec![NDDimension::new(4), NDDimension::new(4)],
1277 NDDataType::UInt8,
1278 );
1279 set_color_mode_attr(&mut arr, NDColorMode::Bayer);
1280 if let NDDataBuffer::U8(ref mut v) = arr.data {
1281 for i in 0..16 {
1282 v[i] = 128;
1283 }
1284 }
1285
1286 let result = proc.process_array(&arr, &pool);
1287 assert_eq!(result.output_arrays.len(), 1);
1288 assert_eq!(result.output_arrays[0].dims.len(), 2);
1289 }
1290
1291 #[test]
1292 fn test_rgb1_to_yuv444_conversion() {
1293 let config = ColorConvertConfig {
1294 target_mode: NDColorMode::YUV444,
1295 bayer_pattern: NDBayerPattern::RGGB,
1296 false_color: 0,
1297 };
1298 let mut proc = ColorConvertProcessor::new(config);
1299 let pool = NDArrayPool::new(1_000_000);
1300
1301 let mut arr = NDArray::new(
1302 vec![
1303 NDDimension::new(3),
1304 NDDimension::new(4),
1305 NDDimension::new(4),
1306 ],
1307 NDDataType::UInt8,
1308 );
1309 if let NDDataBuffer::U8(ref mut v) = arr.data {
1310 for i in 0..v.len() {
1311 v[i] = (i % 256) as u8;
1312 }
1313 }
1314
1315 let result = proc.process_array(&arr, &pool);
1316 assert_eq!(result.output_arrays.len(), 1);
1317 let out = &result.output_arrays[0];
1318 assert_eq!(out.dims.len(), 3);
1319 assert_eq!(out.dims[0].size, 3);
1320 }
1321
1322 #[test]
1323 fn test_yuv422_to_rgb1_conversion() {
1324 let config = ColorConvertConfig {
1325 target_mode: NDColorMode::RGB1,
1326 bayer_pattern: NDBayerPattern::RGGB,
1327 false_color: 0,
1328 };
1329 let mut proc = ColorConvertProcessor::new(config);
1330 let pool = NDArrayPool::new(1_000_000);
1331
1332 let mut arr = NDArray::new(
1334 vec![NDDimension::new(8), NDDimension::new(2)],
1335 NDDataType::UInt8,
1336 );
1337 set_color_mode_attr(&mut arr, NDColorMode::YUV422);
1338 if let NDDataBuffer::U8(ref mut v) = arr.data {
1339 let uyvy: [u8; 16] = [
1341 128, 100, 128, 150, 128, 200, 128, 50, 128, 128, 128, 128, 128, 64, 128, 192,
1342 ];
1343 v[..16].copy_from_slice(&uyvy);
1344 }
1345
1346 let result = proc.process_array(&arr, &pool);
1347 assert_eq!(result.output_arrays.len(), 1);
1348 let out = &result.output_arrays[0];
1349 assert_eq!(out.dims[0].size, 3);
1350 assert_eq!(out.dims[1].size, 4);
1351 assert_eq!(out.dims[2].size, 2);
1352 }
1353
1354 #[test]
1355 fn test_mono_to_yuv422_conversion() {
1356 let config = ColorConvertConfig {
1357 target_mode: NDColorMode::YUV422,
1358 bayer_pattern: NDBayerPattern::RGGB,
1359 false_color: 0,
1360 };
1361 let mut proc = ColorConvertProcessor::new(config);
1362 let pool = NDArrayPool::new(1_000_000);
1363
1364 let mut arr = NDArray::new(
1365 vec![NDDimension::new(4), NDDimension::new(2)],
1366 NDDataType::UInt8,
1367 );
1368 if let NDDataBuffer::U8(ref mut v) = arr.data {
1369 for i in 0..8 {
1370 v[i] = (i * 30) as u8;
1371 }
1372 }
1373
1374 let result = proc.process_array(&arr, &pool);
1375 assert_eq!(result.output_arrays.len(), 1);
1376 let out = &result.output_arrays[0];
1377 assert_eq!(out.dims.len(), 2);
1378 assert_eq!(out.dims[0].size, 8); }
1380
1381 #[test]
1382 fn test_yuv444_to_mono_conversion() {
1383 let config = ColorConvertConfig {
1384 target_mode: NDColorMode::Mono,
1385 bayer_pattern: NDBayerPattern::RGGB,
1386 false_color: 0,
1387 };
1388 let mut proc = ColorConvertProcessor::new(config);
1389 let pool = NDArrayPool::new(1_000_000);
1390
1391 let mut arr = NDArray::new(
1392 vec![
1393 NDDimension::new(3),
1394 NDDimension::new(4),
1395 NDDimension::new(4),
1396 ],
1397 NDDataType::UInt8,
1398 );
1399 set_color_mode_attr(&mut arr, NDColorMode::YUV444);
1400 if let NDDataBuffer::U8(ref mut v) = arr.data {
1401 for i in 0..v.len() {
1402 v[i] = 128;
1403 }
1404 }
1405
1406 let result = proc.process_array(&arr, &pool);
1407 assert_eq!(result.output_arrays.len(), 1);
1408 let out = &result.output_arrays[0];
1409 assert_eq!(out.dims.len(), 2);
1410 assert_eq!(out.dims[0].size, 4);
1411 assert_eq!(out.dims[1].size, 4);
1412 }
1413
1414 #[test]
1415 fn test_detect_color_mode_with_attribute() {
1416 let mut arr = NDArray::new(
1417 vec![NDDimension::new(8), NDDimension::new(2)],
1418 NDDataType::UInt8,
1419 );
1420 assert_eq!(detect_color_mode(&arr), NDColorMode::Mono);
1421
1422 set_color_mode_attr(&mut arr, NDColorMode::YUV422);
1423 assert_eq!(detect_color_mode(&arr), NDColorMode::YUV422);
1424 }
1425
1426 #[test]
1427 fn test_false_color_table_endpoints() {
1428 assert_eq!(RAINBOW_COLOR_MAP[0], [0, 0, 0]);
1432 assert_eq!(RAINBOW_COLOR_MAP[255], [255, 255, 255]);
1433 assert_eq!(IRON_COLOR_MAP[0], [0, 0, 0]);
1434 assert_eq!(IRON_COLOR_MAP[255], [255, 255, 255]);
1435
1436 assert_eq!(RAINBOW_COLOR_MAP[128], [4, 255, 251]);
1439 assert_eq!(IRON_COLOR_MAP[128], [192, 64, 64]);
1440
1441 assert_eq!(false_color_lut(1), Some(&RAINBOW_COLOR_MAP));
1443 assert_eq!(false_color_lut(2), Some(&IRON_COLOR_MAP));
1444 assert_eq!(false_color_lut(0), None);
1445 assert_eq!(false_color_lut(3), None);
1446 }
1447}