-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathlib.rs
223 lines (198 loc) · 6.33 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use image::{io::Reader, ColorType, DynamicImage};
#[derive(Debug, Copy, Clone)]
pub enum TensorType {
F16,
F32,
U8,
I32,
}
#[derive(Debug, Copy, Clone)]
pub enum ColorOrder {
RGB,
BGR,
}
#[derive(Debug, Copy, Clone)]
pub enum MemoryLayout {
Planar,
Interleaved,
}
// Take the image located at 'path', open it, resize it to `height` x `width`, and then convert the
// pixel precision to the type requested. NOTE: this function assumes the image is in standard 8-bit
// RGB format. It should work with standard image formats such as `.jpg`, `.png`, etc.
pub fn convert_image_to_tensor_bytes(
path: &str,
width: u32,
height: u32,
precision: TensorType,
order: ColorOrder,
) -> Result<Vec<u8>, String> {
// Open the file and create the reader.
let raw_file = Reader::open(path)
.map_err(|_| format!("Failed to open the file: {:?}", path))
.unwrap();
// Create the DynamicImage by decoding the image.
let decoded = raw_file
.decode()
.map_err(|_| format!("Failed to decode the file: {:?}", path))
.unwrap();
let color_type = decoded.color();
convert_dynamic_image_to_tensor_bytes(
decoded,
width,
height,
color_type,
precision,
order,
MemoryLayout::Interleaved,
)
}
// Take the image located at 'path', open it, resize it to `height` x `width`, then convert the
// pixel precision to the type requested in planar (not interleave) format. NOTE: this function
// assumes the image is in standard 8-bit RGB format. It should work with standard image formats
// such as `.jpg`, `.png`, etc.
pub fn convert_image_to_planar_tensor_bytes(
path: &str,
width: u32,
height: u32,
precision: TensorType,
order: ColorOrder,
) -> Result<Vec<u8>, String> {
// Open the file and create the reader.
let raw_file = Reader::open(path)
.map_err(|_| format!("Failed to open the file: {:?}", path))
.unwrap();
// Create the DynamicImage by decoding the image.
let decoded = raw_file
.decode()
.map_err(|_| format!("Failed to decode the file: {:?}", path))
.unwrap();
let color_type = decoded.color();
convert_dynamic_image_to_tensor_bytes(
decoded,
width,
height,
color_type,
precision,
order,
MemoryLayout::Planar,
)
}
/// Same as [convert_image_to_tensor_bytes] but accepts a `bytes` slice instead.
pub fn convert_image_bytes_to_tensor_bytes(
bytes: &[u8],
width: u32,
height: u32,
precision: TensorType,
order: ColorOrder,
) -> Result<Vec<u8>, String> {
// Create the DynamicImage by decoding the image.
let decoded = image::load_from_memory(bytes).expect("Unable to load image from bytes.");
convert_dynamic_image_to_tensor_bytes(
decoded,
width,
height,
ColorType::Rgb8,
precision,
order,
MemoryLayout::Interleaved,
)
}
fn convert_dynamic_image_to_tensor_bytes(
image: DynamicImage,
width: u32,
height: u32,
src_color: ColorType,
precision: TensorType,
order: ColorOrder,
layout: MemoryLayout,
) -> Result<Vec<u8>, String> {
// Resize the image to the specified W/H and get an array of u8 RGB values.
let dyn_img: DynamicImage = image.resize_exact(width, height, image::imageops::Triangle);
let mut img_bytes: Vec<u8> = dyn_img.into_bytes();
let src_channels = match src_color {
ColorType::Rgba8 | ColorType::Rgba16 | ColorType::Rgba32F => 4,
ColorType::Rgb8 | ColorType::Rgb16 | ColorType::Rgb32F => 3,
_ => unimplemented!(),
};
if matches!(order, ColorOrder::BGR) {
rgb_to_bgr(&mut img_bytes, src_channels);
}
if matches!(layout, MemoryLayout::Planar) {
img_bytes =
interleave_to_planar(&img_bytes, src_channels, width as usize * height as usize);
}
// Output channel number is always 3 at this moment because we only support RGB and BGR.
Ok(save_bytes(
&img_bytes[..width as usize * height as usize * 3],
precision,
))
}
/// Calculate the expected tensor data size of an image of `width` x `height` with the given
/// `precision`.
pub fn calculate_buffer_size(width: u32, height: u32, precision: TensorType) -> usize {
let bytes_per_pixel = get_bytes_per_pixel(precision);
let pixels: u32 = width * height * 3;
pixels as usize * bytes_per_pixel
}
/// Save the bytes into the specified TensorType format.
fn save_bytes(buffer: &[u8], tt: TensorType) -> Vec<u8> {
let mut out: Vec<u8> = vec![];
for &byte in buffer {
// Split out the bytes based on the TensorType.
let ne_bytes = match tt {
TensorType::F16 => todo!("unable to convert to f16 yet"),
TensorType::F32 => (byte as f32).to_ne_bytes().to_vec(),
TensorType::U8 => (byte).to_ne_bytes().to_vec(),
TensorType::I32 => (byte as i32).to_ne_bytes().to_vec(),
};
for byte in ne_bytes {
out.push(byte);
}
}
out
}
fn get_bytes_per_pixel(precision: TensorType) -> usize {
match precision {
TensorType::F32 | TensorType::I32 => 4,
TensorType::F16 => 4, // Currently Rust doesn't support F16 natively, so we use f32.
TensorType::U8 => 1,
}
}
/// Converts an RGB array to BGR.
fn rgb_to_bgr(buffer: &mut [u8], channels: usize) -> &[u8] {
for i in (0..buffer.len()).step_by(channels) {
buffer.swap(i + 2, i);
}
buffer
}
fn interleave_to_planar(buffer: &[u8], channels: usize, pixels: usize) -> Vec<u8> {
let mut out: Vec<u8> = Vec::with_capacity(buffer.len());
unsafe {
out.set_len(buffer.len());
}
let mut offset = 0;
for i in (0..buffer.len()).step_by(channels) {
for c in 0..channels {
out[pixels * c + offset] = buffer[i + c];
}
offset += 1;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convert_image() {
let path = std::path::Path::new("./../rust/examples/images/0.jpg");
let tensor_bytes = convert_image_to_tensor_bytes(
path.canonicalize().unwrap().to_str().unwrap(),
224,
224,
TensorType::F32,
ColorOrder::RGB,
)
.unwrap();
assert_eq!(&tensor_bytes[..8], [0, 0, 16, 65, 0, 0, 96, 65]);
}
}