image_to_oled/
lib.rs

1use image::{ImageBuffer, Rgb};
2
3pub type Frame = Vec<u8>;
4pub type FrameBuffer = ImageBuffer<Rgb<u8>, Frame>;
5
6/// Converts RGB pixels to a 128x64 SSD1306 OLED byte vector.
7///
8/// `brightness_threshold` is used to determine if a pixel is black or white.
9///
10/// If the brightness of a pixel is above the given threshold the pixel becomes white, else it becomes black.
11///
12/// Example
13///
14/// ```ignore
15/// use image_to_oled::to_oled_bytes;
16///
17/// let image_buffer: ImageBuffer<Rgb<u8>, Vec<u8>> =
18/// ImageBuffer::from_vec(640, 480, vec![155; (1024 * 1024 * 3) as usize]).unwrap();
19/// let bytes = to_oled_bytes(&image_buffer, 100);
20/// ```
21pub fn to_oled_bytes(frame_buffer: &FrameBuffer, brightness_threshold: u8) -> Vec<u8> {
22    let resized_img =
23        image::imageops::resize(frame_buffer, 128, 64, image::imageops::FilterType::Nearest);
24
25    resized_img
26        .chunks(3)
27        .fold(
28            (0, 0, 7_i32, resized_img.len(), Vec::<u8>::new()),
29            |(mut number, mut i, mut byte_index, pixels_len, mut oled_frame), rgb| {
30                // Get the average of the RGB while preventing overflow
31                let avg = rgb.iter().map(|x| *x as u16).sum::<u16>() / 3;
32
33                if avg > brightness_threshold.into() {
34                    number += 2_u8.pow(byte_index as u32);
35                }
36
37                byte_index -= 1;
38
39                // if this was the last pixel of a row or the last pixel of the
40                // image, fill up the rest of our byte with zeros so it always contains 8 bits
41                if (i != 0 && (((i / 3) + 1) % (128)) == 0) || (i == (pixels_len - 3)) {
42                    byte_index = -1;
43                }
44
45                // When there are 8 bits push into Vec and reset counts
46                if byte_index < 0 {
47                    oled_frame.push(number);
48                    number = 0;
49                    byte_index = 7;
50                }
51
52                i += 3;
53
54                (number, i, byte_index, pixels_len, oled_frame)
55            },
56        )
57        .4
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn it_returns_a_byte_vec_of_size_1024_for_any_size_frame_buffer() {
66        for i in [1, 4, 8, 16, 200, 400, 1000, 10000] {
67            let frame_buffer: ImageBuffer<Rgb<u8>, Vec<u8>> =
68                ImageBuffer::from_vec(i, i, vec![0; (i * i * 3) as usize]).unwrap();
69            let result = to_oled_bytes(&frame_buffer, 20);
70
71            assert_eq!(result.len(), 1024);
72        }
73    }
74
75    #[test]
76    fn it_uses_brightness_threshold_to_determine_number() {
77        let frame_buffer: ImageBuffer<Rgb<u8>, Vec<u8>> =
78            ImageBuffer::from_vec(2, 2, vec![30; 12]).unwrap();
79        let black_results = to_oled_bytes(&frame_buffer, 30);
80
81        assert_eq!(black_results, vec![0; 1024]);
82        assert_eq!(black_results.len(), 1024);
83
84        let white_results = to_oled_bytes(&frame_buffer, 20);
85
86        assert_eq!(white_results, vec![255; 1024]);
87        assert_eq!(white_results.len(), 1024);
88    }
89
90    #[test]
91    fn it_does_not_overflow_when_computing_average() {
92        let frame_buffer: ImageBuffer<Rgb<u8>, Vec<u8>> =
93            ImageBuffer::from_vec(640, 480, vec![255; (1024 * 1024 * 3) as usize]).unwrap();
94        let results = to_oled_bytes(&frame_buffer, 30);
95
96        assert_eq!(results, vec![255; 1024]);
97        assert_eq!(results.len(), 1024);
98    }
99}