1use super::core::{Frame, FrameMetadata, ProcessingStage};
8use crate::error::Result;
9use image::{GenericImageView, ImageBuffer, Luma};
10use scirs2_core::ndarray::{Array1, Array2};
11use std::time::Instant;
12
13pub struct GrayscaleStage;
15
16impl ProcessingStage for GrayscaleStage {
17 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
18 if let Some(ref metadata) = frame.metadata {
20 if metadata.channels > 1 {
21 let (height, width) = frame.data.dim();
24 let mut grayscale = Array2::<f32>::zeros((height, width));
25
26 grayscale.assign(&frame.data);
33
34 frame.data = grayscale;
35
36 if let Some(ref mut meta) = frame.metadata {
38 meta.channels = 1;
39 }
40 }
41 }
42
43 Ok(frame)
45 }
46
47 fn name(&self) -> &str {
48 "Grayscale"
49 }
50}
51
52pub struct BlurStage {
54 sigma: f32,
55}
56
57impl BlurStage {
58 pub fn new(sigma: f32) -> Self {
60 Self { sigma }
61 }
62}
63
64impl ProcessingStage for BlurStage {
65 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
66 frame.data = crate::simd_ops::simd_gaussian_blur(&frame.data.view(), self.sigma)?;
68 Ok(frame)
69 }
70
71 fn name(&self) -> &str {
72 "GaussianBlur"
73 }
74}
75
76pub struct EdgeDetectionStage {
78 #[allow(dead_code)]
79 threshold: f32,
80}
81
82impl EdgeDetectionStage {
83 pub fn new(threshold: f32) -> Self {
85 Self { threshold }
86 }
87}
88
89impl ProcessingStage for EdgeDetectionStage {
90 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
91 let (_, _, magnitude) = crate::simd_ops::simd_sobel_gradients(&frame.data.view())?;
93 frame.data = magnitude;
94 Ok(frame)
95 }
96
97 fn name(&self) -> &str {
98 "EdgeDetection"
99 }
100}
101
102pub struct PerspectiveTransformStage {
104 transform: crate::transform::perspective::PerspectiveTransform,
105 output_width: u32,
106 output_height: u32,
107 border_mode: crate::transform::perspective::BorderMode,
108}
109
110impl PerspectiveTransformStage {
111 pub fn new(
113 transform: crate::transform::perspective::PerspectiveTransform,
114 output_width: u32,
115 output_height: u32,
116 border_mode: crate::transform::perspective::BorderMode,
117 ) -> Self {
118 Self {
119 transform,
120 output_width,
121 output_height,
122 border_mode,
123 }
124 }
125}
126
127impl ProcessingStage for PerspectiveTransformStage {
128 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
129 let (height, width) = frame.data.dim();
130
131 let mut img_buf = ImageBuffer::new(width as u32, height as u32);
133
134 for (y, row) in frame.data.rows().into_iter().enumerate() {
135 for (x, &pixel) in row.iter().enumerate() {
136 let gray_value = (pixel * 255.0).clamp(0.0, 255.0) as u8;
137 img_buf.put_pixel(x as u32, y as u32, Luma([gray_value]));
138 }
139 }
140
141 let src_img = image::DynamicImage::ImageLuma8(img_buf);
142
143 let transformed = crate::transform::perspective::warp_perspective_simd(
145 &src_img,
146 &self.transform,
147 Some(self.output_width),
148 Some(self.output_height),
149 self.border_mode,
150 )?;
151
152 let mut output_data =
154 Array2::zeros((self.output_height as usize, self.output_width as usize));
155
156 for y in 0..self.output_height {
157 for x in 0..self.output_width {
158 let pixel = transformed.get_pixel(x, y);
159 let gray_value = pixel[0] as f32 / 255.0;
160 output_data[[y as usize, x as usize]] = gray_value;
161 }
162 }
163
164 frame.data = output_data;
165
166 if let Some(ref mut metadata) = frame.metadata {
168 metadata.width = self.output_width;
169 metadata.height = self.output_height;
170 }
171
172 Ok(frame)
173 }
174
175 fn name(&self) -> &str {
176 "PerspectiveTransform"
177 }
178}
179
180pub struct SimdNormalizationStage;
182
183impl ProcessingStage for SimdNormalizationStage {
184 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
185 frame.data = crate::simd_ops::simd_normalize_image(&frame.data.view())?;
186 Ok(frame)
187 }
188
189 fn name(&self) -> &str {
190 "SimdNormalization"
191 }
192}
193
194pub struct SimdHistogramEqualizationStage {
196 num_bins: usize,
197}
198
199impl SimdHistogramEqualizationStage {
200 pub fn new(num_bins: usize) -> Self {
202 Self { num_bins }
203 }
204}
205
206impl ProcessingStage for SimdHistogramEqualizationStage {
207 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
208 frame.data =
209 crate::simd_ops::simd_histogram_equalization(&frame.data.view(), self.num_bins)?;
210 Ok(frame)
211 }
212
213 fn name(&self) -> &str {
214 "SimdHistogramEqualization"
215 }
216}
217
218pub struct FeatureDetectionStage {
220 detector_type: FeatureDetectorType,
221 #[allow(dead_code)]
222 maxfeatures: usize,
223}
224
225pub enum FeatureDetectorType {
227 Harris {
229 threshold: f32,
231 k: f32,
233 },
234 Fast {
236 threshold: u8,
238 },
239 Sobel,
241}
242
243impl FeatureDetectionStage {
244 pub fn new(detector_type: FeatureDetectorType, maxfeatures: usize) -> Self {
246 Self {
247 detector_type,
248 maxfeatures,
249 }
250 }
251}
252
253impl ProcessingStage for FeatureDetectionStage {
254 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
255 match self.detector_type {
256 FeatureDetectorType::Harris { threshold, k } => {
257 frame.data = self.simd_harris_detection(&frame.data.view(), threshold, k)?;
259 }
260 FeatureDetectorType::Fast { threshold } => {
261 frame.data = self.simd_fast_detection(&frame.data.view(), threshold)?;
263 }
264 FeatureDetectorType::Sobel => {
265 let (_, _, magnitude) = crate::simd_ops::simd_sobel_gradients(&frame.data.view())?;
267 frame.data = magnitude;
268 }
269 }
270
271 Ok(frame)
272 }
273
274 fn name(&self) -> &str {
275 "FeatureDetection"
276 }
277}
278
279impl FeatureDetectionStage {
280 fn simd_harris_detection(
297 &self,
298 image: &scirs2_core::ndarray::ArrayView2<f32>,
299 threshold: f32,
300 k: f32,
301 ) -> Result<Array2<f32>> {
302 use scirs2_core::simd_ops::SimdUnifiedOps;
303
304 let (grad_x, grad_y_, _) = crate::simd_ops::simd_sobel_gradients(image)?;
306
307 let (height, width) = grad_x.dim();
308
309 let mut ixx = Array2::zeros((height, width));
311 let mut iyy = Array2::zeros((height, width));
312 let mut ixy = Array2::zeros((height, width));
313
314 for y in 0..height {
317 let gx_row = grad_x.row(y);
318 let gy_row = grad_y_.row(y);
319
320 let ixx_row = f32::simd_mul(&gx_row, &gx_row);
322 let iyy_row = f32::simd_mul(&gy_row, &gy_row);
323 let ixy_row = f32::simd_mul(&gx_row, &gy_row);
324
325 ixx.row_mut(y).assign(&ixx_row);
327 iyy.row_mut(y).assign(&iyy_row);
328 ixy.row_mut(y).assign(&ixy_row);
329 }
330
331 let window_size = 3;
333 let kernel_weight = 1.0 / (window_size * window_size) as f32;
334
335 let ixx_smooth = self.simd_box_filter(&ixx.view(), window_size, kernel_weight)?;
336 let iyy_smooth = self.simd_box_filter(&iyy.view(), window_size, kernel_weight)?;
337 let ixy_smooth = self.simd_box_filter(&ixy.view(), window_size, kernel_weight)?;
338
339 let mut harris_response = Array2::zeros((height, width));
342
343 for y in 0..height {
344 let ixx_row = ixx_smooth.row(y);
345 let iyy_row = iyy_smooth.row(y);
346 let ixy_row = ixy_smooth.row(y);
347
348 let det_row = f32::simd_sub(
350 &f32::simd_mul(&ixx_row, &iyy_row).view(),
351 &f32::simd_mul(&ixy_row, &ixy_row).view(),
352 );
353
354 let trace_row = f32::simd_add(&ixx_row, &iyy_row);
356 let trace_sq_row = f32::simd_mul(&trace_row.view(), &trace_row.view());
357
358 let k_trace_sq_row = f32::simd_scalar_mul(&trace_sq_row.view(), k);
360 let harris_row = f32::simd_sub(&det_row.view(), &k_trace_sq_row.view());
361
362 harris_response.row_mut(y).assign(&harris_row);
364 }
365
366 let thresholded = harris_response.mapv(|h| if h > threshold { h.max(0.0) } else { 0.0 });
368
369 Ok(thresholded)
370 }
371
372 fn simd_fast_detection(
388 &self,
389 image: &scirs2_core::ndarray::ArrayView2<f32>,
390 threshold: u8,
391 ) -> Result<Array2<f32>> {
392 use scirs2_core::simd_ops::SimdUnifiedOps;
393
394 let (height, width) = image.dim();
395 let mut response = Array2::zeros((height, width));
396 let threshold_f32 = threshold as f32;
397
398 let circle_offsets = [
400 (0, -3),
401 (1, -3),
402 (2, -2),
403 (3, -1),
404 (3, 0),
405 (3, 1),
406 (2, 2),
407 (1, 3),
408 (0, 3),
409 (-1, 3),
410 (-2, 2),
411 (-3, 1),
412 (-3, 0),
413 (-3, -1),
414 (-2, -2),
415 (-1, -3),
416 ];
417
418 const CHUNK_SIZE: usize = 8; for y in 3..height - 3 {
422 let mut x = 3;
423 while x < width - 3 - CHUNK_SIZE {
424 let mut center_pixels = Vec::with_capacity(CHUNK_SIZE);
426 for dx in 0..CHUNK_SIZE {
427 if x + dx < width - 3 {
428 center_pixels.push(image[[y, x + dx]]);
429 }
430 }
431
432 if center_pixels.len() >= 4 {
433 for (i, ¢er_pixel) in center_pixels.iter().enumerate() {
435 let current_x = x + i;
436 let mut consecutive_count = 0;
437 let mut max_consecutive = 0;
438
439 for &(ox, oy) in &circle_offsets {
441 let sample_x = current_x as i32 + ox;
442 let sample_y = y as i32 + oy;
443
444 if sample_x >= 0
445 && sample_x < width as i32
446 && sample_y >= 0
447 && sample_y < height as i32
448 {
449 let sample_pixel = image[[sample_y as usize, sample_x as usize]];
450 let diff = (center_pixel - sample_pixel).abs();
451
452 if diff > threshold_f32 {
453 consecutive_count += 1;
454 max_consecutive = max_consecutive.max(consecutive_count);
455 } else {
456 consecutive_count = 0;
457 }
458 }
459 }
460
461 if max_consecutive >= 9 {
463 response[[y, current_x]] = max_consecutive as f32;
464 }
465 }
466 }
467
468 x += CHUNK_SIZE;
469 }
470
471 while x < width - 3 {
473 let center_pixel = image[[y, x]];
474 let mut consecutive_count = 0;
475 let mut max_consecutive = 0;
476
477 for &(ox, oy) in &circle_offsets {
478 let sample_x = x as i32 + ox;
479 let sample_y = y as i32 + oy;
480
481 if sample_x >= 0
482 && sample_x < width as i32
483 && sample_y >= 0
484 && sample_y < height as i32
485 {
486 let sample_pixel = image[[sample_y as usize, sample_x as usize]];
487 let diff = (center_pixel - sample_pixel).abs();
488
489 if diff > threshold_f32 {
490 consecutive_count += 1;
491 max_consecutive = max_consecutive.max(consecutive_count);
492 } else {
493 consecutive_count = 0;
494 }
495 }
496 }
497
498 if max_consecutive >= 9 {
499 response[[y, x]] = max_consecutive as f32;
500 }
501
502 x += 1;
503 }
504 }
505
506 Ok(response)
507 }
508
509 fn simd_box_filter(
521 &self,
522 image: &scirs2_core::ndarray::ArrayView2<f32>,
523 window_size: usize,
524 kernel_weight: f32,
525 ) -> Result<Array2<f32>> {
526 use scirs2_core::simd_ops::SimdUnifiedOps;
527
528 let (height, width) = image.dim();
529 let mut result = Array2::zeros((height, width));
530 let half_window = window_size / 2;
531
532 let mut horizontal_pass = Array2::zeros((height, width));
535
536 for y in 0..height {
537 for x in half_window..width - half_window {
538 let start_x = x - half_window;
539 let end_x = x + half_window + 1;
540
541 if end_x - start_x >= 4 {
542 let window_data: Vec<f32> = (start_x..end_x).map(|xi| image[[y, xi]]).collect();
544 let window_array = Array1::from_vec(window_data);
545 let sum = f32::simd_sum(&window_array.view());
546 horizontal_pass[[y, x]] = sum * kernel_weight;
547 } else {
548 let sum: f32 = (start_x..end_x).map(|xi| image[[y, xi]]).sum();
550 horizontal_pass[[y, x]] = sum * kernel_weight;
551 }
552 }
553 }
554
555 for y in half_window..height - half_window {
557 for x in 0..width {
558 let start_y = y - half_window;
559 let end_y = y + half_window + 1;
560
561 if end_y - start_y >= 4 {
562 let window_data: Vec<f32> = (start_y..end_y)
564 .map(|yi| horizontal_pass[[yi, x]])
565 .collect();
566 let window_array = Array1::from_vec(window_data);
567 let sum = f32::simd_sum(&window_array.view());
568 result[[y, x]] = sum * kernel_weight;
569 } else {
570 let sum: f32 = (start_y..end_y).map(|yi| horizontal_pass[[yi, x]]).sum();
572 result[[y, x]] = sum * kernel_weight;
573 }
574 }
575 }
576
577 Ok(result)
578 }
579}
580
581pub struct FrameBufferStage {
583 buffer: std::collections::VecDeque<Array2<f32>>,
584 buffer_size: usize,
585 operation: BufferOperation,
586}
587
588pub enum BufferOperation {
590 TemporalAverage,
592 BackgroundSubtraction,
594 FrameDifference,
596}
597
598impl FrameBufferStage {
599 pub fn new(_buffersize: usize, operation: BufferOperation) -> Self {
601 Self {
602 buffer: std::collections::VecDeque::with_capacity(_buffersize),
603 buffer_size: _buffersize,
604 operation,
605 }
606 }
607}
608
609impl ProcessingStage for FrameBufferStage {
610 fn process(&mut self, mut frame: Frame) -> Result<Frame> {
611 self.buffer.push_back(frame.data.clone());
613 if self.buffer.len() > self.buffer_size {
614 self.buffer.pop_front();
615 }
616
617 match self.operation {
619 BufferOperation::TemporalAverage => {
620 if !self.buffer.is_empty() {
621 let mut avg = Array2::<f32>::zeros(frame.data.dim());
622 for buffered_frame in &self.buffer {
623 avg += buffered_frame;
624 }
625 frame.data = avg / self.buffer.len() as f32;
626 }
627 }
628 BufferOperation::BackgroundSubtraction => {
629 if self.buffer.len() >= self.buffer_size {
630 let mut background = Array2::<f32>::zeros(frame.data.dim());
632 for buffered_frame in &self.buffer {
633 background += buffered_frame;
634 }
635 background /= self.buffer.len() as f32;
636 frame.data = (&frame.data - &background).mapv(|x| x.abs());
637 }
638 }
639 BufferOperation::FrameDifference => {
640 if self.buffer.len() >= 2 {
641 let prev_frame = &self.buffer[self.buffer.len() - 2];
642 frame.data = (&frame.data - prev_frame).mapv(|x| x.abs());
643 }
644 }
645 }
646
647 Ok(frame)
648 }
649
650 fn name(&self) -> &str {
651 match self.operation {
652 BufferOperation::TemporalAverage => "TemporalAverage",
653 BufferOperation::BackgroundSubtraction => "BackgroundSubtraction",
654 BufferOperation::FrameDifference => "FrameDifference",
655 }
656 }
657}