1use std::sync::Arc;
2
3#[cfg(feature = "parallel")]
4use rayon::prelude::*;
5#[cfg(feature = "parallel")]
6use crate::par_util;
7
8use ad_core::color::{self, NDColorMode, NDBayerPattern};
9use ad_core::ndarray::{NDArray, NDDataBuffer, NDDataType, NDDimension};
10use ad_core::ndarray_pool::NDArrayPool;
11use ad_core::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 n = w * h;
23 let src_vals: Vec<f64> = (0..n).map(|i| src.data.get_as_f64(i).unwrap_or(0.0)).collect();
24 let get_val = |x: usize, y: usize| -> f64 { src_vals[y * w + x] };
25
26 let mut r = vec![0.0f64; n];
27 let mut g = vec![0.0f64; n];
28 let mut b = vec![0.0f64; n];
29
30 let (r_row_even, r_col_even) = match pattern {
32 NDBayerPattern::RGGB => (true, true),
33 NDBayerPattern::GBRG => (true, false),
34 NDBayerPattern::GRBG => (false, true),
35 NDBayerPattern::BGGR => (false, false),
36 };
37
38 let demosaic_row = |y: usize, r_row: &mut [f64], g_row: &mut [f64], b_row: &mut [f64]| {
40 let even_row = (y % 2 == 0) == r_row_even;
41 for x in 0..w {
42 let val = get_val(x, y);
43 let even_col = (x % 2 == 0) == r_col_even;
44
45 match (even_row, even_col) {
46 (true, true) => {
47 r_row[x] = val;
48 let mut gsum = 0.0; let mut gc = 0;
49 if x > 0 { gsum += get_val(x - 1, y); gc += 1; }
50 if x < w - 1 { gsum += get_val(x + 1, y); gc += 1; }
51 if y > 0 { gsum += get_val(x, y - 1); gc += 1; }
52 if y < h - 1 { gsum += get_val(x, y + 1); gc += 1; }
53 g_row[x] = if gc > 0 { gsum / gc as f64 } else { 0.0 };
54 let mut bsum = 0.0; let mut bc = 0;
55 if x > 0 && y > 0 { bsum += get_val(x - 1, y - 1); bc += 1; }
56 if x < w - 1 && y > 0 { bsum += get_val(x + 1, y - 1); bc += 1; }
57 if x > 0 && y < h - 1 { bsum += get_val(x - 1, y + 1); bc += 1; }
58 if x < w - 1 && y < h - 1 { bsum += get_val(x + 1, y + 1); bc += 1; }
59 b_row[x] = if bc > 0 { bsum / bc as f64 } else { 0.0 };
60 }
61 (true, false) | (false, true) => {
62 g_row[x] = val;
63 if even_row {
64 let mut rsum = 0.0; let mut rc = 0;
65 if x > 0 { rsum += get_val(x - 1, y); rc += 1; }
66 if x < w - 1 { rsum += get_val(x + 1, y); rc += 1; }
67 r_row[x] = if rc > 0 { rsum / rc as f64 } else { 0.0 };
68 let mut bsum = 0.0; let mut bc = 0;
69 if y > 0 { bsum += get_val(x, y - 1); bc += 1; }
70 if y < h - 1 { bsum += get_val(x, y + 1); bc += 1; }
71 b_row[x] = if bc > 0 { bsum / bc as f64 } else { 0.0 };
72 } else {
73 let mut bsum = 0.0; let mut bc = 0;
74 if x > 0 { bsum += get_val(x - 1, y); bc += 1; }
75 if x < w - 1 { bsum += get_val(x + 1, y); bc += 1; }
76 b_row[x] = if bc > 0 { bsum / bc as f64 } else { 0.0 };
77 let mut rsum = 0.0; let mut rc = 0;
78 if y > 0 { rsum += get_val(x, y - 1); rc += 1; }
79 if y < h - 1 { rsum += get_val(x, y + 1); rc += 1; }
80 r_row[x] = if rc > 0 { rsum / rc as f64 } else { 0.0 };
81 }
82 }
83 (false, false) => {
84 b_row[x] = val;
85 let mut gsum = 0.0; let mut gc = 0;
86 if x > 0 { gsum += get_val(x - 1, y); gc += 1; }
87 if x < w - 1 { gsum += get_val(x + 1, y); gc += 1; }
88 if y > 0 { gsum += get_val(x, y - 1); gc += 1; }
89 if y < h - 1 { gsum += get_val(x, y + 1); gc += 1; }
90 g_row[x] = if gc > 0 { gsum / gc as f64 } else { 0.0 };
91 let mut rsum = 0.0; let mut rc = 0;
92 if x > 0 && y > 0 { rsum += get_val(x - 1, y - 1); rc += 1; }
93 if x < w - 1 && y > 0 { rsum += get_val(x + 1, y - 1); rc += 1; }
94 if x > 0 && y < h - 1 { rsum += get_val(x - 1, y + 1); rc += 1; }
95 if x < w - 1 && y < h - 1 { rsum += get_val(x + 1, y + 1); rc += 1; }
96 r_row[x] = if rc > 0 { rsum / rc as f64 } else { 0.0 };
97 }
98 }
99 }
100 };
101
102 #[cfg(feature = "parallel")]
103 let use_parallel = par_util::should_parallelize(n);
104 #[cfg(not(feature = "parallel"))]
105 let use_parallel = false;
106
107 if use_parallel {
108 #[cfg(feature = "parallel")]
109 {
110 let r_rows: Vec<&mut [f64]> = r.chunks_mut(w).collect();
112 let g_rows: Vec<&mut [f64]> = g.chunks_mut(w).collect();
113 let b_rows: Vec<&mut [f64]> = b.chunks_mut(w).collect();
114
115 par_util::thread_pool().install(|| {
116 r_rows.into_par_iter()
117 .zip(g_rows.into_par_iter())
118 .zip(b_rows.into_par_iter())
119 .enumerate()
120 .for_each(|(y, ((r_row, g_row), b_row))| {
121 demosaic_row(y, r_row, g_row, b_row);
122 });
123 });
124 }
125 } else {
126 for y in 0..h {
127 let row_start = y * w;
128 let row_end = row_start + w;
129 demosaic_row(
130 y,
131 &mut r[row_start..row_end],
132 &mut g[row_start..row_end],
133 &mut b[row_start..row_end],
134 );
135 }
136 }
137
138 let out_data = match src.data.data_type() {
140 NDDataType::UInt8 => {
141 let mut out = vec![0u8; n * 3];
142 for i in 0..n {
143 out[i * 3] = r[i].clamp(0.0, 255.0) as u8;
144 out[i * 3 + 1] = g[i].clamp(0.0, 255.0) as u8;
145 out[i * 3 + 2] = b[i].clamp(0.0, 255.0) as u8;
146 }
147 NDDataBuffer::U8(out)
148 }
149 NDDataType::UInt16 => {
150 let mut out = vec![0u16; n * 3];
151 for i in 0..n {
152 out[i * 3] = r[i].clamp(0.0, 65535.0) as u16;
153 out[i * 3 + 1] = g[i].clamp(0.0, 65535.0) as u16;
154 out[i * 3 + 2] = b[i].clamp(0.0, 65535.0) as u16;
155 }
156 NDDataBuffer::U16(out)
157 }
158 _ => return None,
159 };
160
161 let dims = vec![NDDimension::new(3), NDDimension::new(w), NDDimension::new(h)];
162 let mut arr = NDArray::new(dims, src.data.data_type());
163 arr.data = out_data;
164 arr.unique_id = src.unique_id;
165 arr.timestamp = src.timestamp;
166 arr.attributes = src.attributes.clone();
167 Some(arr)
168}
169
170fn jet_colormap() -> [[u8; 3]; 256] {
174 let mut lut = [[0u8; 3]; 256];
175 for i in 0..256 {
176 let v = i as f64 / 255.0;
177 let r = (1.5 - (4.0 * v - 3.0).abs()).clamp(0.0, 1.0);
179 let g = (1.5 - (4.0 * v - 2.0).abs()).clamp(0.0, 1.0);
180 let b = (1.5 - (4.0 * v - 1.0).abs()).clamp(0.0, 1.0);
181 lut[i] = [(r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8];
182 }
183 lut
184}
185
186fn false_color_mono_to_rgb1(src: &NDArray) -> Option<NDArray> {
191 if src.dims.len() != 2 || src.data.data_type() != NDDataType::UInt8 {
192 return None;
193 }
194
195 let w = src.dims[0].size;
196 let h = src.dims[1].size;
197 let n = w * h;
198 let lut = jet_colormap();
199
200 let src_slice = src.data.as_u8_slice();
201 let mut out = vec![0u8; n * 3];
202 for i in 0..n {
203 let val = src_slice[i] as usize;
204 let [r, g, b] = lut[val];
205 out[i * 3] = r;
206 out[i * 3 + 1] = g;
207 out[i * 3 + 2] = b;
208 }
209
210 let dims = vec![NDDimension::new(3), NDDimension::new(w), NDDimension::new(h)];
211 let mut arr = NDArray::new(dims, NDDataType::UInt8);
212 arr.data = NDDataBuffer::U8(out);
213 arr.unique_id = src.unique_id;
214 arr.timestamp = src.timestamp;
215 arr.attributes = src.attributes.clone();
216 Some(arr)
217}
218
219fn detect_color_mode(array: &NDArray) -> NDColorMode {
225 if let Some(attr) = array.attributes.get("ColorMode") {
226 if let Some(v) = attr.value.as_i64() {
227 return NDColorMode::from_i32(v as i32);
228 }
229 }
230 match array.dims.len() {
231 0 | 1 => NDColorMode::Mono,
232 2 => NDColorMode::Mono,
233 3 => {
234 if array.dims[0].size == 3 {
235 NDColorMode::RGB1
236 } else if array.dims[1].size == 3 {
237 NDColorMode::RGB2
238 } else if array.dims[2].size == 3 {
239 NDColorMode::RGB3
240 } else {
241 NDColorMode::Mono
242 }
243 }
244 _ => NDColorMode::Mono,
245 }
246}
247
248#[derive(Debug, Clone)]
250pub struct ColorConvertConfig {
251 pub target_mode: NDColorMode,
252 pub bayer_pattern: NDBayerPattern,
253 pub false_color: bool,
254}
255
256pub struct ColorConvertProcessor {
258 config: ColorConvertConfig,
259}
260
261impl ColorConvertProcessor {
262 pub fn new(config: ColorConvertConfig) -> Self {
263 Self { config }
264 }
265}
266
267impl NDPluginProcess for ColorConvertProcessor {
268 fn process_array(&mut self, array: &NDArray, _pool: &NDArrayPool) -> ProcessResult {
269 let src_mode = detect_color_mode(array);
270 let target = self.config.target_mode;
271
272 if src_mode == target {
274 return ProcessResult::arrays(vec![Arc::new(array.clone())]);
275 }
276
277 let rgb1 = match src_mode {
279 NDColorMode::RGB1 => Some(array.clone()),
280 NDColorMode::Mono => {
281 if self.config.false_color {
282 false_color_mono_to_rgb1(array)
283 .or_else(|| color::mono_to_rgb1(array).ok())
284 } else {
285 color::mono_to_rgb1(array).ok()
286 }
287 }
288 NDColorMode::Bayer => bayer_to_rgb1(array, self.config.bayer_pattern),
289 NDColorMode::RGB2 | NDColorMode::RGB3 => {
290 color::convert_rgb_layout(array, src_mode, NDColorMode::RGB1).ok()
291 }
292 NDColorMode::YUV444 => color::yuv444_to_rgb1(array).ok(),
293 NDColorMode::YUV422 => color::yuv422_to_rgb1(array).ok(),
294 NDColorMode::YUV411 => color::yuv411_to_rgb1(array).ok(),
295 };
296
297 let rgb1 = match rgb1 {
298 Some(r) => r,
299 None => return ProcessResult::empty(),
300 };
301
302 let result = match target {
304 NDColorMode::RGB1 => Some(rgb1),
305 NDColorMode::Mono => color::rgb1_to_mono(&rgb1).ok(),
306 NDColorMode::Bayer => None,
307 NDColorMode::RGB2 | NDColorMode::RGB3 => {
308 color::convert_rgb_layout(&rgb1, NDColorMode::RGB1, target).ok()
309 }
310 NDColorMode::YUV444 => color::rgb1_to_yuv444(&rgb1).ok(),
311 NDColorMode::YUV422 => color::rgb1_to_yuv422(&rgb1).ok(),
312 NDColorMode::YUV411 => color::rgb1_to_yuv411(&rgb1).ok(),
313 };
314
315 match result {
316 Some(out) => ProcessResult::arrays(vec![Arc::new(out)]),
317 None => ProcessResult::empty(),
318 }
319 }
320
321 fn plugin_type(&self) -> &str {
322 "NDPluginColorConvert"
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_bayer_to_rgb1_basic() {
332 let mut arr = NDArray::new(
334 vec![NDDimension::new(4), NDDimension::new(4)],
335 NDDataType::UInt8,
336 );
337 if let NDDataBuffer::U8(ref mut v) = arr.data {
338 for i in 0..16 {
340 v[i] = 128;
341 }
342 }
343
344 let rgb = bayer_to_rgb1(&arr, NDBayerPattern::RGGB).unwrap();
345 assert_eq!(rgb.dims.len(), 3);
346 assert_eq!(rgb.dims[0].size, 3); assert_eq!(rgb.dims[1].size, 4); assert_eq!(rgb.dims[2].size, 4); }
350
351 #[test]
352 fn test_color_convert_processor_bayer() {
353 let config = ColorConvertConfig {
354 target_mode: NDColorMode::RGB1,
355 bayer_pattern: NDBayerPattern::RGGB,
356 false_color: false,
357 };
358 let mut proc = ColorConvertProcessor::new(config);
359 let pool = NDArrayPool::new(1_000_000);
360
361 let mut arr = NDArray::new(
362 vec![NDDimension::new(4), NDDimension::new(4)],
363 NDDataType::UInt8,
364 );
365 if let NDDataBuffer::U8(ref mut v) = arr.data {
366 for i in 0..16 {
367 v[i] = 128;
368 }
369 }
370
371 let result = proc.process_array(&arr, &pool);
372 assert_eq!(result.output_arrays.len(), 1);
373 assert_eq!(result.output_arrays[0].dims[0].size, 3); }
375
376 #[test]
377 fn test_false_color_conversion() {
378 let config = ColorConvertConfig {
379 target_mode: NDColorMode::RGB1,
380 bayer_pattern: NDBayerPattern::RGGB,
381 false_color: true,
382 };
383 let mut proc = ColorConvertProcessor::new(config);
384 let pool = NDArrayPool::new(1_000_000);
385
386 let mut arr = NDArray::new(
388 vec![NDDimension::new(4), NDDimension::new(4)],
389 NDDataType::UInt8,
390 );
391 if let NDDataBuffer::U8(ref mut v) = arr.data {
392 for i in 0..16 {
393 v[i] = (i * 17) as u8; }
395 }
396
397 let result = proc.process_array(&arr, &pool);
398 assert_eq!(result.output_arrays.len(), 1);
399 let out = &result.output_arrays[0];
400 assert_eq!(out.dims.len(), 3);
401 assert_eq!(out.dims[0].size, 3); assert_eq!(out.dims[1].size, 4); assert_eq!(out.dims[2].size, 4); let lut = jet_colormap();
407 if let NDDataBuffer::U8(ref v) = out.data {
408 assert_eq!(v[0], lut[0][0]); assert_eq!(v[1], lut[0][1]); assert_eq!(v[2], lut[0][2]); let last = 15 * 3;
414 assert_eq!(v[last], lut[255][0]); assert_eq!(v[last + 1], lut[255][1]); assert_eq!(v[last + 2], lut[255][2]); } else {
418 panic!("expected UInt8 output");
419 }
420 }
421
422 #[test]
423 fn test_rgb1_to_rgb2_conversion() {
424 let config = ColorConvertConfig {
425 target_mode: NDColorMode::RGB2,
426 bayer_pattern: NDBayerPattern::RGGB,
427 false_color: false,
428 };
429 let mut proc = ColorConvertProcessor::new(config);
430 let pool = NDArrayPool::new(1_000_000);
431
432 let mut arr = NDArray::new(
434 vec![NDDimension::new(3), NDDimension::new(4), NDDimension::new(4)],
435 NDDataType::UInt8,
436 );
437 if let NDDataBuffer::U8(ref mut v) = arr.data {
438 for i in 0..v.len() {
439 v[i] = (i % 256) as u8;
440 }
441 }
442
443 let result = proc.process_array(&arr, &pool);
444 assert_eq!(result.output_arrays.len(), 1);
445 let out = &result.output_arrays[0];
446 assert_eq!(out.dims.len(), 3);
447 assert_eq!(out.dims[1].size, 3);
449 }
450
451 #[test]
452 fn test_rgb2_to_mono_conversion() {
453 let config = ColorConvertConfig {
454 target_mode: NDColorMode::Mono,
455 bayer_pattern: NDBayerPattern::RGGB,
456 false_color: false,
457 };
458 let mut proc = ColorConvertProcessor::new(config);
459 let pool = NDArrayPool::new(1_000_000);
460
461 let mut arr = NDArray::new(
463 vec![NDDimension::new(4), NDDimension::new(3), NDDimension::new(4)],
464 NDDataType::UInt8,
465 );
466 if let NDDataBuffer::U8(ref mut v) = arr.data {
467 for i in 0..v.len() {
468 v[i] = 128;
469 }
470 }
471
472 let result = proc.process_array(&arr, &pool);
473 assert_eq!(result.output_arrays.len(), 1);
474 let out = &result.output_arrays[0];
475 assert_eq!(out.dims.len(), 2);
477 }
478
479 #[test]
480 fn test_detect_color_mode() {
481 let arr2d = NDArray::new(
483 vec![NDDimension::new(4), NDDimension::new(4)],
484 NDDataType::UInt8,
485 );
486 assert_eq!(detect_color_mode(&arr2d), NDColorMode::Mono);
487
488 let arr_rgb1 = NDArray::new(
490 vec![NDDimension::new(3), NDDimension::new(4), NDDimension::new(4)],
491 NDDataType::UInt8,
492 );
493 assert_eq!(detect_color_mode(&arr_rgb1), NDColorMode::RGB1);
494
495 let arr_rgb2 = NDArray::new(
497 vec![NDDimension::new(4), NDDimension::new(3), NDDimension::new(4)],
498 NDDataType::UInt8,
499 );
500 assert_eq!(detect_color_mode(&arr_rgb2), NDColorMode::RGB2);
501
502 let arr_rgb3 = NDArray::new(
504 vec![NDDimension::new(4), NDDimension::new(4), NDDimension::new(3)],
505 NDDataType::UInt8,
506 );
507 assert_eq!(detect_color_mode(&arr_rgb3), NDColorMode::RGB3);
508 }
509
510 #[test]
511 fn test_same_mode_passthrough() {
512 let config = ColorConvertConfig {
513 target_mode: NDColorMode::Mono,
514 bayer_pattern: NDBayerPattern::RGGB,
515 false_color: false,
516 };
517 let mut proc = ColorConvertProcessor::new(config);
518 let pool = NDArrayPool::new(1_000_000);
519
520 let mut arr = NDArray::new(
522 vec![NDDimension::new(4), NDDimension::new(4)],
523 NDDataType::UInt8,
524 );
525 arr.unique_id = 42;
526 if let NDDataBuffer::U8(ref mut v) = arr.data {
527 for i in 0..16 {
528 v[i] = i as u8;
529 }
530 }
531
532 let result = proc.process_array(&arr, &pool);
533 assert_eq!(result.output_arrays.len(), 1);
534 assert_eq!(result.output_arrays[0].unique_id, 42);
535 assert_eq!(result.output_arrays[0].dims.len(), 2);
536 }
537
538 fn set_color_mode_attr(arr: &mut NDArray, mode: NDColorMode) {
539 use ad_core::attributes::{NDAttrSource, NDAttrValue, NDAttribute};
540 arr.attributes.add(NDAttribute {
541 name: "ColorMode".to_string(),
542 description: String::new(),
543 source: NDAttrSource::Driver,
544 value: NDAttrValue::Int32(mode as i32),
545 });
546 }
547
548 #[test]
549 fn test_bayer_to_mono_via_rgb1() {
550 let config = ColorConvertConfig {
551 target_mode: NDColorMode::Mono,
552 bayer_pattern: NDBayerPattern::RGGB,
553 false_color: false,
554 };
555 let mut proc = ColorConvertProcessor::new(config);
556 let pool = NDArrayPool::new(1_000_000);
557
558 let mut arr = NDArray::new(
559 vec![NDDimension::new(4), NDDimension::new(4)],
560 NDDataType::UInt8,
561 );
562 set_color_mode_attr(&mut arr, NDColorMode::Bayer);
563 if let NDDataBuffer::U8(ref mut v) = arr.data {
564 for i in 0..16 { v[i] = 128; }
565 }
566
567 let result = proc.process_array(&arr, &pool);
568 assert_eq!(result.output_arrays.len(), 1);
569 assert_eq!(result.output_arrays[0].dims.len(), 2);
570 }
571
572 #[test]
573 fn test_rgb1_to_yuv444_conversion() {
574 let config = ColorConvertConfig {
575 target_mode: NDColorMode::YUV444,
576 bayer_pattern: NDBayerPattern::RGGB,
577 false_color: false,
578 };
579 let mut proc = ColorConvertProcessor::new(config);
580 let pool = NDArrayPool::new(1_000_000);
581
582 let mut arr = NDArray::new(
583 vec![NDDimension::new(3), NDDimension::new(4), NDDimension::new(4)],
584 NDDataType::UInt8,
585 );
586 if let NDDataBuffer::U8(ref mut v) = arr.data {
587 for i in 0..v.len() { v[i] = (i % 256) as u8; }
588 }
589
590 let result = proc.process_array(&arr, &pool);
591 assert_eq!(result.output_arrays.len(), 1);
592 let out = &result.output_arrays[0];
593 assert_eq!(out.dims.len(), 3);
594 assert_eq!(out.dims[0].size, 3);
595 }
596
597 #[test]
598 fn test_yuv422_to_rgb1_conversion() {
599 let config = ColorConvertConfig {
600 target_mode: NDColorMode::RGB1,
601 bayer_pattern: NDBayerPattern::RGGB,
602 false_color: false,
603 };
604 let mut proc = ColorConvertProcessor::new(config);
605 let pool = NDArrayPool::new(1_000_000);
606
607 let mut arr = NDArray::new(
609 vec![NDDimension::new(8), NDDimension::new(2)],
610 NDDataType::UInt8,
611 );
612 set_color_mode_attr(&mut arr, NDColorMode::YUV422);
613 if let NDDataBuffer::U8(ref mut v) = arr.data {
614 let uyvy: [u8; 16] = [
616 128, 100, 128, 150, 128, 200, 128, 50,
617 128, 128, 128, 128, 128, 64, 128, 192,
618 ];
619 v[..16].copy_from_slice(&uyvy);
620 }
621
622 let result = proc.process_array(&arr, &pool);
623 assert_eq!(result.output_arrays.len(), 1);
624 let out = &result.output_arrays[0];
625 assert_eq!(out.dims[0].size, 3);
626 assert_eq!(out.dims[1].size, 4);
627 assert_eq!(out.dims[2].size, 2);
628 }
629
630 #[test]
631 fn test_mono_to_yuv422_conversion() {
632 let config = ColorConvertConfig {
633 target_mode: NDColorMode::YUV422,
634 bayer_pattern: NDBayerPattern::RGGB,
635 false_color: false,
636 };
637 let mut proc = ColorConvertProcessor::new(config);
638 let pool = NDArrayPool::new(1_000_000);
639
640 let mut arr = NDArray::new(
641 vec![NDDimension::new(4), NDDimension::new(2)],
642 NDDataType::UInt8,
643 );
644 if let NDDataBuffer::U8(ref mut v) = arr.data {
645 for i in 0..8 { v[i] = (i * 30) as u8; }
646 }
647
648 let result = proc.process_array(&arr, &pool);
649 assert_eq!(result.output_arrays.len(), 1);
650 let out = &result.output_arrays[0];
651 assert_eq!(out.dims.len(), 2);
652 assert_eq!(out.dims[0].size, 8); }
654
655 #[test]
656 fn test_yuv444_to_mono_conversion() {
657 let config = ColorConvertConfig {
658 target_mode: NDColorMode::Mono,
659 bayer_pattern: NDBayerPattern::RGGB,
660 false_color: false,
661 };
662 let mut proc = ColorConvertProcessor::new(config);
663 let pool = NDArrayPool::new(1_000_000);
664
665 let mut arr = NDArray::new(
666 vec![NDDimension::new(3), NDDimension::new(4), NDDimension::new(4)],
667 NDDataType::UInt8,
668 );
669 set_color_mode_attr(&mut arr, NDColorMode::YUV444);
670 if let NDDataBuffer::U8(ref mut v) = arr.data {
671 for i in 0..v.len() { v[i] = 128; }
672 }
673
674 let result = proc.process_array(&arr, &pool);
675 assert_eq!(result.output_arrays.len(), 1);
676 let out = &result.output_arrays[0];
677 assert_eq!(out.dims.len(), 2);
678 assert_eq!(out.dims[0].size, 4);
679 assert_eq!(out.dims[1].size, 4);
680 }
681
682 #[test]
683 fn test_detect_color_mode_with_attribute() {
684 let mut arr = NDArray::new(
685 vec![NDDimension::new(8), NDDimension::new(2)],
686 NDDataType::UInt8,
687 );
688 assert_eq!(detect_color_mode(&arr), NDColorMode::Mono);
689
690 set_color_mode_attr(&mut arr, NDColorMode::YUV422);
691 assert_eq!(detect_color_mode(&arr), NDColorMode::YUV422);
692 }
693
694 #[test]
695 fn test_jet_colormap_endpoints() {
696 let lut = jet_colormap();
697 assert_eq!(lut[0][0], 0); assert_eq!(lut[0][1], 0); assert_eq!(lut[0][2], 127); assert_eq!(lut[255][0], 127); assert_eq!(lut[255][1], 0); assert_eq!(lut[255][2], 0); }
707}