bpm_ocr/
lib.rs

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    /// Attempts to extract a blood pressure reading from a photo file of a blood pressure monitor screen
59    /// * `filename` - the path to the photo file
60    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    /// Attempts to extract a blood pressure reading from a byte buffer containing a photo file of a blood pressure monitor screen
71    /// * `filename` - the byte buffer with the photo file
72    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}