1use std::sync::Arc;
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, DebuggerTrace, 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 debugging_session: DebuggerTrace<T>,
22}
23
24impl<T: BpmOcrDebugOutputter> BloodPressureReadingExtractor<T> {
25 pub fn new(debugger_session: DebuggerTrace<T>) -> Self {
26 let screen_extractor = LcdScreenExtractor::new(
27 Arc::clone(&debugger_session.debugger),
28 &debugger_session.unique_trace_name,
29 );
30 let screen_number_extractor = LcdNumberExtractor::new(
31 Arc::clone(&debugger_session.debugger),
32 &debugger_session.unique_trace_name,
33 );
34
35 BloodPressureReadingExtractor {
36 screen_extractor,
37 screen_number_extractor,
38 debugging_session: debugger_session,
39 }
40 }
41
42 fn process_image(self: &Self, image: &Mat) -> Result<BloodPressureReading, ProcessingError> {
43 self.debugging_session
44 .debugger
45 .debug_original_picture(&self.debugging_session.unique_trace_name, &image)?;
46
47 let mut resized_image = Mat::default();
48
49 let interpolation: i32 = 0;
50 imgproc::resize(
51 &image,
52 &mut resized_image,
53 Size::new(800, 800),
54 0.,
55 0.,
56 interpolation,
57 )?;
58
59 let birdseye_lcd_only = self.screen_extractor.extract_lcd(&resized_image)?;
60
61 let reading = self
62 .screen_number_extractor
63 .extract_reading(&birdseye_lcd_only)?;
64
65 Ok(reading)
66 }
67}
68
69pub fn get_reading_from_file<T: BpmOcrDebugOutputter>(
73 filename: &str,
74 debugger: DebuggerTrace<T>,
75) -> Result<BloodPressureReading, ProcessingError> {
76 let extractor: BloodPressureReadingExtractor<T> = BloodPressureReadingExtractor::new(debugger);
77
78 let gray_scale_mode: i32 = ImreadModes::IMREAD_GRAYSCALE.into();
79 let image = imgcodecs::imread(filename, gray_scale_mode)?;
80
81 extractor.process_image(&image)
82}
83
84pub fn get_reading_from_buffer<T: BpmOcrDebugOutputter>(
88 file_contents: Vec<u8>,
89 debugger: DebuggerTrace<T>,
90) -> Result<BloodPressureReading, ProcessingError> {
91 let extractor: BloodPressureReadingExtractor<T> = BloodPressureReadingExtractor::new(debugger);
92
93 let contents = Vector::from_slice(&file_contents);
94 let image = imgcodecs::imdecode(&contents, ImreadModes::IMREAD_GRAYSCALE.into())?;
95
96 extractor.process_image(&image)
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::debug::TempFolderDebugger;
103
104 #[test]
105 fn test_success_photo_at_angle() {
106 let debug_session: DebuggerTrace<TempFolderDebugger> =
107 DebuggerTrace::temp_folder_session("test_success");
108
109 let testfile = Vec::from(include_bytes!("./test_resources/example_at_angle.jpg"));
110
111 let expected_result = BloodPressureReading {
112 systolic: 133,
113 diastolic: 93,
114 pulse: 65,
115 };
116
117 let result = get_reading_from_buffer(testfile, debug_session).unwrap();
118
119 assert_eq!(result, expected_result);
120 }
121
122 #[test]
123 fn test_success_topdown_photo() {
124 let debug_session: DebuggerTrace<TempFolderDebugger> =
125 DebuggerTrace::temp_folder_session("test_topdown_photo");
126
127 let testfile = Vec::from(include_bytes!("./test_resources/example_top_down.jpg"));
128
129 let expected_result = BloodPressureReading {
130 systolic: 131,
131 diastolic: 88,
132 pulse: 77,
133 };
134
135 let result = get_reading_from_buffer(testfile, debug_session).unwrap();
136
137 assert_eq!(result, expected_result);
138 }
139
140 #[test]
141 fn test_with_2_digit() {
142 let debug_session: DebuggerTrace<TempFolderDebugger> =
143 DebuggerTrace::temp_folder_session("contour_candidates");
144
145 let testfile = Vec::from(include_bytes!("./test_resources/contour_candidates.jpeg"));
146
147 let expected_result = BloodPressureReading {
148 systolic: 123,
149 diastolic: 85,
150 pulse: 68,
151 };
152
153 let result = get_reading_from_buffer(testfile, debug_session).unwrap();
154
155 assert_eq!(result, expected_result);
156 }
157}