1use std::rc::Rc;
2
3use opencv::core::{Mat, Size, Vector};
4use opencv::imgcodecs::ImreadModes;
5use opencv::{imgcodecs, imgproc};
6
7use crate::debug::BpmOcrDebugOutputter;
8use crate::lcd_number_extractor::LcdNumberExtractor;
9use crate::lcd_screen_extractor::LcdScreenExtractor;
10use crate::models::{BloodPressureReading, ProcessingError};
11pub mod debug;
12mod digit_extractor;
13mod lcd_number_extractor;
14mod lcd_screen_extractor;
15pub mod models;
16mod rectangle;
17
18pub struct BloodPressureReadingExtractor<T: BpmOcrDebugOutputter> {
19 screen_extractor: LcdScreenExtractor<T>,
20 screen_number_extractor: LcdNumberExtractor<T>,
21}
22
23impl<T: BpmOcrDebugOutputter> BloodPressureReadingExtractor<T> {
24 pub fn new(debugger: T) -> Self {
25 let shared_debugger = Rc::new(debugger);
26
27 let screen_extractor = LcdScreenExtractor::new(&shared_debugger);
28 let screen_number_extractor = LcdNumberExtractor::new(&shared_debugger);
29
30 BloodPressureReadingExtractor {
31 screen_extractor,
32 screen_number_extractor,
33 }
34 }
35
36 fn process_image(self: &Self, image: &Mat) -> Result<BloodPressureReading, ProcessingError> {
37 let mut resized_image = Mat::default();
38
39 let interpolation: i32 = 0;
40 imgproc::resize(
41 &image,
42 &mut resized_image,
43 Size::new(800, 800),
44 0.,
45 0.,
46 interpolation,
47 )?;
48
49 let birdseye_lcd_only = self.screen_extractor.extract_lcd(&resized_image)?;
50
51 let reading = self
52 .screen_number_extractor
53 .extract_reading(&birdseye_lcd_only)?;
54
55 Ok(reading)
56 }
57
58 pub fn get_reading_from_file(
61 self: &Self,
62 filename: &str,
63 ) -> Result<BloodPressureReading, ProcessingError> {
64 let gray_scale_mode: i32 = ImreadModes::IMREAD_GRAYSCALE.into();
65 let image = imgcodecs::imread(filename, gray_scale_mode)?;
66
67 self.process_image(&image)
68 }
69
70 pub fn get_reading_from_buffer(
73 self: &Self,
74 file_contents: Vec<u8>,
75 ) -> Result<BloodPressureReading, ProcessingError> {
76 let contents = Vector::from_slice(&file_contents);
77 let image = imgcodecs::imdecode(&contents, ImreadModes::IMREAD_GRAYSCALE.into())?;
78
79 self.process_image(&image)
80 }
81}
82
83
84#[cfg(test)]
85mod tests {
86 use crate::debug::UnsafeTempFolderDebugger;
87
88 use super::*;
89
90 #[test]
91 fn test_success_photo_at_angle() {
92 let debugger: UnsafeTempFolderDebugger = UnsafeTempFolderDebugger::new("test_success", true);
93 let testfile = Vec::from(include_bytes!("./test_resources/example_at_angle.jpg"));
94 let extractor = BloodPressureReadingExtractor::new(debugger);
95
96 let expected_result = BloodPressureReading {
97 systolic: 133,
98 diastolic: 93,
99 pulse: 65
100 };
101
102 let result = extractor.get_reading_from_buffer(testfile).unwrap();
103
104 assert_eq!(result, expected_result);
105 }
106
107 #[test]
108 fn test_success_topdown_photo() {
109 let debugger: UnsafeTempFolderDebugger = UnsafeTempFolderDebugger::new("test_topdown_photo", true);
110 let testfile = Vec::from(include_bytes!("./test_resources/example_top_down.jpg"));
111 let extractor = BloodPressureReadingExtractor::new(debugger);
112
113 let expected_result = BloodPressureReading {
114 systolic: 131,
115 diastolic: 88,
116 pulse: 77
117 };
118
119 let result = extractor.get_reading_from_buffer(testfile).unwrap();
120
121 assert_eq!(result, expected_result);
122 }
123
124
125}