1use crate::error::{Result, VisionError};
7use image::{DynamicImage, Rgb, RgbImage};
8use scirs2_core::ndarray::{Array2, Array3, Array4};
9
10#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum SegmentationArchitecture {
13 DeepLabV3Plus,
15 UNet,
17 FCN,
19 MaskRCNN,
21 SegFormer,
23}
24
25#[derive(Debug, Clone)]
27pub struct SegmentationClass {
28 pub id: usize,
30 pub name: String,
32 pub color: (u8, u8, u8),
34}
35
36#[derive(Debug, Clone)]
38pub struct SegmentationResult {
39 pub class_map: Array2<usize>,
41 pub confidence: Array3<f32>,
43 pub classes: Vec<SegmentationClass>,
45}
46
47impl SegmentationResult {
48 pub fn to_color_image(&self) -> Result<RgbImage> {
50 let (height, width) = self.class_map.dim();
51 let mut img = RgbImage::new(width as u32, height as u32);
52
53 for y in 0..height {
54 for x in 0..width {
55 let class_id = self.class_map[[y, x]];
56 let color = if class_id < self.classes.len() {
57 let (r, g, b) = self.classes[class_id].color;
58 Rgb([r, g, b])
59 } else {
60 Rgb([0, 0, 0])
61 };
62 img.put_pixel(x as u32, y as u32, color);
63 }
64 }
65
66 Ok(img)
67 }
68
69 pub fn get_class_confidence(&self, y: usize, x: usize, class_id: usize) -> Option<f32> {
71 if y < self.confidence.dim().0
72 && x < self.confidence.dim().1
73 && class_id < self.confidence.dim().2
74 {
75 Some(self.confidence[[y, x, class_id]])
76 } else {
77 None
78 }
79 }
80
81 pub fn refine_with_crf(
83 &mut self,
84 original_img: &DynamicImage,
85 iterations: usize,
86 ) -> Result<()> {
87 let (height, width) = self.class_map.dim();
89 let num_classes = self.classes.len();
90
91 for _iter in 0..iterations {
92 let mut new_confidence = self.confidence.clone();
93
94 for y in 1..height - 1 {
95 for x in 1..width - 1 {
96 for c in 0..num_classes {
98 let mut sum = self.confidence[[y, x, c]];
99 let mut count = 1.0;
100
101 for (dy, dx) in &[(-1, 0), (1, 0), (0, -1), (0, 1)] {
103 let ny = (y as i32 + dy) as usize;
104 let nx = (x as i32 + dx) as usize;
105
106 sum += self.confidence[[ny, nx, c]] * 0.1;
107 count += 0.1;
108 }
109
110 new_confidence[[y, x, c]] = sum / count;
111 }
112
113 let sum: f32 = (0..num_classes).map(|c| new_confidence[[y, x, c]]).sum();
115 if sum > 0.0 {
116 for c in 0..num_classes {
117 new_confidence[[y, x, c]] /= sum;
118 }
119 }
120 }
121 }
122
123 self.confidence = new_confidence;
124
125 for y in 0..height {
127 for x in 0..width {
128 let mut max_conf = 0.0f32;
129 let mut max_class = 0;
130 for c in 0..num_classes {
131 if self.confidence[[y, x, c]] > max_conf {
132 max_conf = self.confidence[[y, x, c]];
133 max_class = c;
134 }
135 }
136 self.class_map[[y, x]] = max_class;
137 }
138 }
139 }
140
141 Ok(())
142 }
143}
144
145pub struct DeepLabV3Plus {
147 num_classes: usize,
148 input_size: (usize, usize),
149 classes: Vec<SegmentationClass>,
150}
151
152impl DeepLabV3Plus {
153 pub fn new(
161 num_classes: usize,
162 input_size: (usize, usize),
163 classes: Vec<SegmentationClass>,
164 ) -> Self {
165 Self {
166 num_classes,
167 input_size,
168 classes,
169 }
170 }
171
172 pub fn segment(&self, img: &DynamicImage) -> Result<SegmentationResult> {
174 let input_tensor = self.preprocess(img)?;
176
177 let output_tensor = self.forward(&input_tensor)?;
179
180 self.postprocess(&output_tensor)
182 }
183
184 fn preprocess(&self, img: &DynamicImage) -> Result<Array4<f32>> {
186 let resized = img.resize_exact(
187 self.input_size.1 as u32,
188 self.input_size.0 as u32,
189 image::imageops::FilterType::Lanczos3,
190 );
191
192 let rgb = resized.to_rgb8();
193 let (width, height) = rgb.dimensions();
194
195 let mut tensor = Array4::zeros((1, 3, height as usize, width as usize));
197
198 for y in 0..height {
199 for x in 0..width {
200 let pixel = rgb.get_pixel(x, y);
201 tensor[[0, 0, y as usize, x as usize]] = (pixel[0] as f32 / 127.5) - 1.0;
203 tensor[[0, 1, y as usize, x as usize]] = (pixel[1] as f32 / 127.5) - 1.0;
204 tensor[[0, 2, y as usize, x as usize]] = (pixel[2] as f32 / 127.5) - 1.0;
205 }
206 }
207
208 Ok(tensor)
209 }
210
211 fn forward(&self, input: &Array4<f32>) -> Result<Array4<f32>> {
213 let (batch, _, height, width) = input.dim();
216
217 let mut output = Array4::zeros((batch, self.num_classes, height, width));
219
220 for b in 0..batch {
222 for c in 0..self.num_classes {
223 for y in 0..height {
224 for x in 0..width {
225 let val = if c == 0 {
227 0.8 } else {
229 0.2 / (self.num_classes - 1) as f32
230 };
231 output[[b, c, y, x]] = val;
232 }
233 }
234 }
235 }
236
237 Ok(output)
238 }
239
240 fn postprocess(&self, output: &Array4<f32>) -> Result<SegmentationResult> {
242 let (_, num_classes, height, width) = output.dim();
243
244 let mut class_map = Array2::zeros((height, width));
246 let mut confidence = Array3::zeros((height, width, num_classes));
247
248 for y in 0..height {
249 for x in 0..width {
250 let mut max_val = f32::NEG_INFINITY;
252 let mut max_class = 0;
253 let mut sum = 0.0f32;
254
255 let mut scores = vec![0.0f32; num_classes];
257 for c in 0..num_classes {
258 scores[c] = output[[0, c, y, x]].exp();
259 sum += scores[c];
260 }
261
262 for c in 0..num_classes {
263 scores[c] /= sum;
264 confidence[[y, x, c]] = scores[c];
265
266 if scores[c] > max_val {
267 max_val = scores[c];
268 max_class = c;
269 }
270 }
271
272 class_map[[y, x]] = max_class;
273 }
274 }
275
276 Ok(SegmentationResult {
277 class_map,
278 confidence,
279 classes: self.classes.clone(),
280 })
281 }
282}
283
284pub struct UNet {
286 num_classes: usize,
287 input_size: (usize, usize),
288 classes: Vec<SegmentationClass>,
289}
290
291impl UNet {
292 pub fn new(
294 num_classes: usize,
295 input_size: (usize, usize),
296 classes: Vec<SegmentationClass>,
297 ) -> Self {
298 Self {
299 num_classes,
300 input_size,
301 classes,
302 }
303 }
304
305 pub fn segment(&self, img: &DynamicImage) -> Result<SegmentationResult> {
307 let input_tensor = self.preprocess(img)?;
309 let output_tensor = self.forward(&input_tensor)?;
310 self.postprocess(&output_tensor)
311 }
312
313 fn preprocess(&self, img: &DynamicImage) -> Result<Array4<f32>> {
314 let resized = img.resize_exact(
316 self.input_size.1 as u32,
317 self.input_size.0 as u32,
318 image::imageops::FilterType::Lanczos3,
319 );
320
321 let rgb = resized.to_rgb8();
322 let (width, height) = rgb.dimensions();
323 let mut tensor = Array4::zeros((1, 3, height as usize, width as usize));
324
325 for y in 0..height {
326 for x in 0..width {
327 let pixel = rgb.get_pixel(x, y);
328 tensor[[0, 0, y as usize, x as usize]] = pixel[0] as f32 / 255.0;
329 tensor[[0, 1, y as usize, x as usize]] = pixel[1] as f32 / 255.0;
330 tensor[[0, 2, y as usize, x as usize]] = pixel[2] as f32 / 255.0;
331 }
332 }
333
334 Ok(tensor)
335 }
336
337 fn forward(&self, input: &Array4<f32>) -> Result<Array4<f32>> {
338 let (batch, _, height, width) = input.dim();
340 Ok(Array4::zeros((batch, self.num_classes, height, width)))
341 }
342
343 fn postprocess(&self, output: &Array4<f32>) -> Result<SegmentationResult> {
344 let (_, num_classes, height, width) = output.dim();
345 let class_map = Array2::zeros((height, width));
346 let confidence = Array3::zeros((height, width, num_classes));
347
348 Ok(SegmentationResult {
349 class_map,
350 confidence,
351 classes: self.classes.clone(),
352 })
353 }
354}
355
356pub struct FCN {
358 num_classes: usize,
359 variant: FCNVariant,
360 classes: Vec<SegmentationClass>,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq)]
365pub enum FCNVariant {
366 FCN32s,
368 FCN16s,
370 FCN8s,
372}
373
374impl FCN {
375 pub fn new(num_classes: usize, variant: FCNVariant, classes: Vec<SegmentationClass>) -> Self {
377 Self {
378 num_classes,
379 variant,
380 classes,
381 }
382 }
383
384 pub fn segment(&self, img: &DynamicImage) -> Result<SegmentationResult> {
386 let (height, width) = (img.height() as usize, img.width() as usize);
388 let class_map = Array2::zeros((height, width));
389 let confidence = Array3::zeros((height, width, self.num_classes));
390
391 Ok(SegmentationResult {
392 class_map,
393 confidence,
394 classes: self.classes.clone(),
395 })
396 }
397}
398
399pub fn create_pascal_voc_classes() -> Vec<SegmentationClass> {
401 vec![
402 SegmentationClass {
403 id: 0,
404 name: "background".to_string(),
405 color: (0, 0, 0),
406 },
407 SegmentationClass {
408 id: 1,
409 name: "aeroplane".to_string(),
410 color: (128, 0, 0),
411 },
412 SegmentationClass {
413 id: 2,
414 name: "bicycle".to_string(),
415 color: (0, 128, 0),
416 },
417 SegmentationClass {
418 id: 3,
419 name: "bird".to_string(),
420 color: (128, 128, 0),
421 },
422 SegmentationClass {
423 id: 4,
424 name: "boat".to_string(),
425 color: (0, 0, 128),
426 },
427 SegmentationClass {
428 id: 5,
429 name: "bottle".to_string(),
430 color: (128, 0, 128),
431 },
432 SegmentationClass {
433 id: 6,
434 name: "bus".to_string(),
435 color: (0, 128, 128),
436 },
437 SegmentationClass {
438 id: 7,
439 name: "car".to_string(),
440 color: (128, 128, 128),
441 },
442 SegmentationClass {
443 id: 8,
444 name: "cat".to_string(),
445 color: (64, 0, 0),
446 },
447 SegmentationClass {
448 id: 9,
449 name: "chair".to_string(),
450 color: (192, 0, 0),
451 },
452 SegmentationClass {
453 id: 10,
454 name: "cow".to_string(),
455 color: (64, 128, 0),
456 },
457 SegmentationClass {
458 id: 11,
459 name: "diningtable".to_string(),
460 color: (192, 128, 0),
461 },
462 SegmentationClass {
463 id: 12,
464 name: "dog".to_string(),
465 color: (64, 0, 128),
466 },
467 SegmentationClass {
468 id: 13,
469 name: "horse".to_string(),
470 color: (192, 0, 128),
471 },
472 SegmentationClass {
473 id: 14,
474 name: "motorbike".to_string(),
475 color: (64, 128, 128),
476 },
477 SegmentationClass {
478 id: 15,
479 name: "person".to_string(),
480 color: (192, 128, 128),
481 },
482 SegmentationClass {
483 id: 16,
484 name: "pottedplant".to_string(),
485 color: (0, 64, 0),
486 },
487 SegmentationClass {
488 id: 17,
489 name: "sheep".to_string(),
490 color: (128, 64, 0),
491 },
492 SegmentationClass {
493 id: 18,
494 name: "sofa".to_string(),
495 color: (0, 192, 0),
496 },
497 SegmentationClass {
498 id: 19,
499 name: "train".to_string(),
500 color: (128, 192, 0),
501 },
502 SegmentationClass {
503 id: 20,
504 name: "tvmonitor".to_string(),
505 color: (0, 64, 128),
506 },
507 ]
508}
509
510pub fn create_cityscapes_classes() -> Vec<SegmentationClass> {
512 vec![
513 SegmentationClass {
514 id: 0,
515 name: "road".to_string(),
516 color: (128, 64, 128),
517 },
518 SegmentationClass {
519 id: 1,
520 name: "sidewalk".to_string(),
521 color: (244, 35, 232),
522 },
523 SegmentationClass {
524 id: 2,
525 name: "building".to_string(),
526 color: (70, 70, 70),
527 },
528 SegmentationClass {
529 id: 3,
530 name: "wall".to_string(),
531 color: (102, 102, 156),
532 },
533 SegmentationClass {
534 id: 4,
535 name: "fence".to_string(),
536 color: (190, 153, 153),
537 },
538 SegmentationClass {
539 id: 5,
540 name: "pole".to_string(),
541 color: (153, 153, 153),
542 },
543 SegmentationClass {
544 id: 6,
545 name: "traffic_light".to_string(),
546 color: (250, 170, 30),
547 },
548 SegmentationClass {
549 id: 7,
550 name: "traffic_sign".to_string(),
551 color: (220, 220, 0),
552 },
553 SegmentationClass {
554 id: 8,
555 name: "vegetation".to_string(),
556 color: (107, 142, 35),
557 },
558 SegmentationClass {
559 id: 9,
560 name: "terrain".to_string(),
561 color: (152, 251, 152),
562 },
563 SegmentationClass {
564 id: 10,
565 name: "sky".to_string(),
566 color: (70, 130, 180),
567 },
568 SegmentationClass {
569 id: 11,
570 name: "person".to_string(),
571 color: (220, 20, 60),
572 },
573 SegmentationClass {
574 id: 12,
575 name: "rider".to_string(),
576 color: (255, 0, 0),
577 },
578 SegmentationClass {
579 id: 13,
580 name: "car".to_string(),
581 color: (0, 0, 142),
582 },
583 SegmentationClass {
584 id: 14,
585 name: "truck".to_string(),
586 color: (0, 0, 70),
587 },
588 SegmentationClass {
589 id: 15,
590 name: "bus".to_string(),
591 color: (0, 60, 100),
592 },
593 SegmentationClass {
594 id: 16,
595 name: "train".to_string(),
596 color: (0, 80, 100),
597 },
598 SegmentationClass {
599 id: 17,
600 name: "motorcycle".to_string(),
601 color: (0, 0, 230),
602 },
603 SegmentationClass {
604 id: 18,
605 name: "bicycle".to_string(),
606 color: (119, 11, 32),
607 },
608 ]
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614 use image::RgbImage;
615
616 #[test]
617 fn test_deeplab_creation() {
618 let classes = create_pascal_voc_classes();
619 let model = DeepLabV3Plus::new(21, (512, 512), classes);
620 assert_eq!(model.num_classes, 21);
621 }
622
623 #[test]
624 fn test_unet_creation() {
625 let classes = vec![
626 SegmentationClass {
627 id: 0,
628 name: "background".to_string(),
629 color: (0, 0, 0),
630 },
631 SegmentationClass {
632 id: 1,
633 name: "foreground".to_string(),
634 color: (255, 255, 255),
635 },
636 ];
637 let model = UNet::new(2, (256, 256), classes);
638 assert_eq!(model.num_classes, 2);
639 }
640
641 #[test]
642 fn test_fcn_creation() {
643 let classes = create_pascal_voc_classes();
644 let model = FCN::new(21, FCNVariant::FCN8s, classes);
645 assert_eq!(model.num_classes, 21);
646 }
647
648 #[test]
649 fn test_segmentation_result_to_color() {
650 let class_map = Array2::zeros((10, 10));
651 let confidence = Array3::zeros((10, 10, 2));
652 let classes = vec![
653 SegmentationClass {
654 id: 0,
655 name: "bg".to_string(),
656 color: (0, 0, 0),
657 },
658 SegmentationClass {
659 id: 1,
660 name: "fg".to_string(),
661 color: (255, 255, 255),
662 },
663 ];
664
665 let result = SegmentationResult {
666 class_map,
667 confidence,
668 classes,
669 };
670
671 let color_img = result.to_color_image();
672 assert!(color_img.is_ok());
673 }
674
675 #[test]
676 fn test_pascal_voc_classes() {
677 let classes = create_pascal_voc_classes();
678 assert_eq!(classes.len(), 21);
679 assert_eq!(classes[0].name, "background");
680 }
681
682 #[test]
683 fn test_cityscapes_classes() {
684 let classes = create_cityscapes_classes();
685 assert_eq!(classes.len(), 19);
686 assert_eq!(classes[0].name, "road");
687 }
688
689 #[test]
690 fn test_deeplab_segment() {
691 let classes = create_pascal_voc_classes();
692 let model = DeepLabV3Plus::new(21, (256, 256), classes);
693 let img = DynamicImage::ImageRgb8(RgbImage::new(256, 256));
694
695 let result = model.segment(&img);
696 assert!(result.is_ok());
697 }
698}