data_access_layer/
images.rs

1//! # Image Buffer
2//! In this module we are reading the image to a buffer and calculate the RGB indexes for each pixel.
3use image::io::Reader as ImageReader;
4use image::{ImageBuffer, Rgb, DynamicImage};
5use crate::tags::SurgeryStep;
6use std::io::{self, Write};
7
8
9/// Represents the indexes of the red, green and blue components of a pixel.
10/// 
11/// # Fields
12/// * `red` - The index of the red component in relation to the (x, y) coordinates of the pixel.
13/// * `green` - The index of the green component in relation to the (x, y) coordinates of the pixel.
14/// * `blue` - The index of the blue component in relation to the (x, y) coordinates of the pixel.
15#[derive(Debug)]
16struct RgbIndexes {
17    red: usize,
18    green: usize,
19    blue: usize,
20}
21
22
23/// Calculates the RGB indexes for a given pixel in relation to the (x, y) coordinates of the pixel.
24/// 
25/// # Arguments
26/// * `x` - The x coordinate of the pixel.
27/// * `y` - The y coordinate of the pixel.
28/// * `total_width` - The total width of the image frame.
29/// * `total_height` - The total height of the image frame.
30/// 
31/// # Returns
32/// A RgbIndexes struct containing the indexes of the red, green and blue components of the pixel.
33fn calculate_rgb_index(x: usize, y: usize, total_width: usize, total_height: usize) -> RgbIndexes {
34    RgbIndexes {
35        red: 0 * total_height * total_width + y * total_width + x,
36        green: 1 * total_height * total_width + y * total_width + x,
37        blue: 2 * total_height * total_width + y * total_width + x,
38    }
39}
40
41
42/// Reads an RGB image from a file and returns the raw data in 1D form that can be mapped as a 3D
43/// array by using the `calculate_rgb_index` function.
44/// 
45/// # Arguments
46/// * `path` - The path to the image file.
47/// * `height` - The total height of the image.
48/// * `width` - The total width of the image.
49/// 
50/// # Returns
51/// A 1D array containing the raw RGB data of the image (flatterned).
52pub fn read_rgb_image(path: String, height: usize, width: usize) -> Vec<u8> {
53    // let height: usize = 480;
54    // let width: usize = 853;
55    let depth: usize = 3;
56
57    let img: DynamicImage = ImageReader::open(path).unwrap().decode().unwrap();
58    let resized_img: DynamicImage = img.resize_exact(width as u32, height as u32, image::imageops::FilterType::Nearest);
59
60    // Convert to RGB and flatten to array if necessary
61    let rgb_img: ImageBuffer<Rgb<u8>, Vec<u8>> = resized_img.to_rgb8();
62
63    let mut raw_data = vec![0u8; depth * height * width]; // 3 channels, 480 height, 853 width
64
65    for chunk in rgb_img.enumerate_pixels() {
66        let x: u32 = chunk.0;
67        let y: u32 = chunk.1;
68        let pixel: &Rgb<u8> = chunk.2; // [u8, u8, u8]
69
70        let indexes = calculate_rgb_index(x as usize, y as usize, width, height);
71
72        raw_data[indexes.red as usize] = pixel[0]; // store red component
73        raw_data[indexes.green as usize] = pixel[1]; // store green component
74        raw_data[indexes.blue as usize] = pixel[2]; // store blue component
75    }
76    raw_data
77}
78
79
80/// Writes a frame to the standard output.
81/// 
82/// # Arguments
83/// * `data` - The raw data of the frame.
84/// * `tag` - The tag associated with the frame.
85pub fn write_frame_to_std_out(data: Vec<u8>, tag: SurgeryStep) {
86    let stdout = io::stdout();
87    let mut handle = stdout.lock();
88
89    // Write the tag as a 2-byte integer
90    handle.write_all(&(tag.to_u8() as u16).to_le_bytes()).unwrap();
91
92    // Write the len as a 4-byte integer
93    handle.write_all(&(data.len() as u32).to_le_bytes()).unwrap();
94
95    // Write each byte in data as a 2-byte integer
96    for byte in data {
97        handle.write_all(&(byte as u16).to_le_bytes()).unwrap();
98    }
99
100    handle.flush().unwrap();
101}
102
103
104#[cfg(test)]
105mod tests {
106
107    use super::*;
108    use serde::Deserialize;
109
110    #[derive(Debug, Deserialize)]
111    struct DummyJson {
112        data: Vec<u8>,
113    }
114
115    #[test]
116    fn test_read_image() {
117        let _data = read_rgb_image("./data_stash/images/169_6300.jpg".to_string(), 480, 853);
118    }
119
120    #[test]
121    fn test_calculate_rgb_index() {
122
123        // This will give x y chunks of 50 and an entire rgb image of 150
124        let total_height = 5;
125        let total_width = 10;
126
127        let indexes = calculate_rgb_index(0, 0, total_width, total_height);
128        assert_eq!(&0, &indexes.red);
129        assert_eq!(&50, &indexes.green);
130        assert_eq!(&100, &indexes.blue);
131
132        let indexes = calculate_rgb_index(1, 0, total_width, total_height);
133        assert_eq!(&1, &indexes.red);
134        assert_eq!(&51, &indexes.green);
135        assert_eq!(&101, &indexes.blue);
136
137        let indexes = calculate_rgb_index(2, 0, total_width, total_height);
138        assert_eq!(&2, &indexes.red);
139        assert_eq!(&52, &indexes.green);
140        assert_eq!(&102, &indexes.blue);
141
142        let indexes = calculate_rgb_index(0, 1, total_width, total_height);
143        assert_eq!(&10, &indexes.red);
144        assert_eq!(&60, &indexes.green);
145        assert_eq!(&110, &indexes.blue);
146
147        let indexes = calculate_rgb_index(0, 2, total_width, total_height);
148        assert_eq!(&20, &indexes.red);
149        assert_eq!(&70, &indexes.green);
150        assert_eq!(&120, &indexes.blue);
151    }
152
153    #[test]
154    fn test_test_calculate_rgb_index_quality_control() {
155        let raw_data = std::fs::read_to_string("./data_stash/images/dummy_rgb_data.json").unwrap();
156        let data: DummyJson = serde_json::from_str(&raw_data).unwrap();
157
158        // This will give x y chunks of 50 and an entire rgb image of 150
159        let total_height = 5;
160        let total_width = 10;
161
162        let index = calculate_rgb_index(0, 0, total_width, total_height);
163        assert_eq!(&data.data[index.red], &111); // z = 0
164        assert_eq!(&data.data[index.green], &208); // z = 1
165        assert_eq!(&data.data[index.blue], &12); // z = 2
166
167        let index = calculate_rgb_index(5, 3, total_width, total_height);
168        assert_eq!(&data.data[index.red], &65);
169        assert_eq!(&data.data[index.green], &7);
170        assert_eq!(&data.data[index.blue], &193);
171
172        let index = calculate_rgb_index(8, 2, total_width, total_height);
173        assert_eq!(&data.data[index.red], &253);
174        assert_eq!(&data.data[index.green], &133);
175        assert_eq!(&data.data[index.blue], &115);
176    }
177}