realsense_rust/frame/pixel.rs
1//! Type for representing the various pixel formats.
2//!
3//! Correct formats are determined by the device type and the streaming profile.
4//! For detailed pixel format information, check the
5//! [Intel RealSense SDK code](https://github.com/IntelRealSense/librealsense/blob/4f37f2ef0874c1716bce223b20e46d00532ffb04/wrappers/nodejs/index.js#L3865).
6
7use crate::kind::Rs2Format;
8use std::{os::raw::c_void, slice};
9
10/// Type for representing the various pixel formats.
11#[derive(Debug)]
12pub enum PixelKind<'a> {
13 /// 32-bit `y0, u, y1, v` data for every two pixels.
14 /// Similar to YUV422 but packed in a different order - see [this link](https://en.wikipedia.org/wiki/YUV).
15 Yuyv {
16 /// The Y / luma value for a given pixel
17 y: &'a u8,
18 /// The U-chroma value for a given pixel
19 u: &'a u8,
20 /// The V-chroma value for a given pixel
21 v: &'a u8,
22 },
23 /// Similar to the standard YUYV pixel format, but packed in a different order.
24 Uyvy {
25 /// The Y / luma value for a given pixel
26 y: &'a u8,
27 /// The U-chroma value for a given pixel
28 u: &'a u8,
29 /// The V-chroma value for a given pixel
30 v: &'a u8,
31 },
32 /// 8-bit blue, green, and red channels -- suitable for OpenCV.
33 Bgr8 {
34 /// The blue component of the BGR pixel
35 b: &'a u8,
36 /// The green component of the BGR pixel
37 g: &'a u8,
38 /// The red component of the BGR pixel
39 r: &'a u8,
40 },
41 /// 8-bit blue, green, and red channels + constant alpha channel equal to FF.
42 Bgra8 {
43 /// The blue component of the BGRA pixel
44 b: &'a u8,
45 /// The green component of the BGRA pixel
46 g: &'a u8,
47 /// The red component of the BGRA pixel
48 r: &'a u8,
49 /// The alpha component of the BGRA pixel
50 a: &'a u8,
51 },
52 /// 8-bit red, green and blue channels.
53 Rgb8 {
54 /// The red component of the RGB pixel
55 r: &'a u8,
56 /// The green component of the RGB pixel
57 g: &'a u8,
58 /// The blue component of the RGB pixel
59 b: &'a u8,
60 },
61 /// 8-bit red, green and blue channels + constant alpha channel equal to FF.
62 Rgba8 {
63 /// The red component of the RGBA pixel
64 r: &'a u8,
65 /// The green component of the RGBA pixel
66 g: &'a u8,
67 /// The blue component of the RGBA pixel
68 b: &'a u8,
69 /// The alpha component of the RGBA pixel
70 a: &'a u8,
71 },
72 /// 8-bit raw image.
73 Raw8 {
74 /// The single luma value for a RAW8 image
75 val: &'a u8,
76 },
77 /// 8-bit per-pixel grayscale image.
78 Y8 {
79 /// The single luma value for a Y-channel only image
80 y: &'a u8,
81 },
82 /// 16-bit per-pixel grayscale image.
83 Y16 {
84 /// The single luma value for a Y-channel only image
85 y: &'a u16,
86 },
87 /// 16-bit linear depth values. The depth is meters is equal to depth scale * pixel value.
88 Z16 {
89 /// Depth value in millimetres
90 depth: &'a u16,
91 },
92 /// 32-bit float-point depth distance value.
93 Distance {
94 /// Distance from camera origin in metres
95 distance: &'a f32,
96 },
97 /// 32-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth.
98 Disparity32 {
99 /// The disparity relative to the opposite eye in a depth camera.
100 disparity: &'a f32,
101 },
102 /// 32-bit floating point 3D coordinates.
103 Xyz32f {
104 /// The X-coordinate
105 x: &'a f32,
106 /// The Y-coordinate
107 y: &'a f32,
108 /// The Z-coordinate
109 z: &'a f32,
110 },
111}
112
113/// Method to retrieve a pixel from a given rs2_frame in the requested Pixel format.
114///
115/// # Safety
116///
117/// This method should only be called from the ImageFrame types themselves, as this
118/// is the only place where proper pointer management happens.
119#[inline]
120pub(crate) unsafe fn get_pixel<'a>(
121 format: Rs2Format,
122 data_size_in_bytes: usize,
123 data: *const c_void,
124 stride_in_bytes: usize,
125 col: usize,
126 row: usize,
127) -> PixelKind<'a> {
128 // Realsense stores frame data in row-major format. Normally, we would offset into a
129 // uniform array in column major format with the following equation:
130 //
131 // offset = row * width + column
132 //
133 // The assumption here being that it is a uniform array. See individual comments below for
134 // how each offset equation differs.
135 //
136 // NOTE; You _could_ still represent this same pointer arithmetic in row-major form, but be
137 // warned that the equations will look fairly different.
138 //
139 match format {
140 // YUYV is not uniform since it encapsulates two pixels over 32 bits (four u8
141 // values). Instead, we can index YUYV (and UYVY) as follows:
142 //
143 // offset = (row * stride) + (col / 2) * 4
144 //
145 // The strange part here is the (col / 2) * 4. This is done because on odd rows we
146 // don't want to offset to the next Y value, but rather take the full YUYV and pick
147 // the correct Y depending on whether the row is even or odd.
148 //
149 // NOTE: Order matters because we are taking advantage of integer division here.
150 //
151 Rs2Format::Yuyv => {
152 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
153 let offset = (row * stride_in_bytes) + (col / 2) * 4;
154
155 let y = if row % 2 == 0 {
156 slice.get_unchecked(offset)
157 } else {
158 slice.get_unchecked(offset + 2)
159 };
160
161 PixelKind::Yuyv {
162 y,
163 u: slice.get_unchecked(offset + 1),
164 v: slice.get_unchecked(offset + 3),
165 }
166 }
167 // UYVY follows from the same exact pattern we use for YUYV, since it's more or less a
168 // re-ordering of the underlying data.
169 //
170 Rs2Format::Uyvy => {
171 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
172 let offset = (row * stride_in_bytes) + (col / 2) * 4;
173
174 let y = if row % 2 == 0 {
175 slice.get_unchecked(offset + 1)
176 } else {
177 slice.get_unchecked(offset + 3)
178 };
179
180 PixelKind::Uyvy {
181 y,
182 u: slice.get_unchecked(offset),
183 v: slice.get_unchecked(offset + 2),
184 }
185 }
186 // For BGR / RGB, we do a similar trick, but since pixels aren't interleaved as they
187 // are with YUYV / UYVY, the multipliers for column and row offsets can be uniform.
188 //
189 Rs2Format::Bgr8 => {
190 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
191 let offset = (row * stride_in_bytes) + (col * 3);
192
193 PixelKind::Bgr8 {
194 b: slice.get_unchecked(offset),
195 g: slice.get_unchecked(offset + 1),
196 r: slice.get_unchecked(offset + 2),
197 }
198 }
199 // BGRA8 is more or less the same as BGR8, except we use 4 as a multiplier.
200 //
201 Rs2Format::Bgra8 => {
202 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
203 let offset = (row * stride_in_bytes) + (col * 4);
204
205 PixelKind::Bgra8 {
206 b: slice.get_unchecked(offset),
207 g: slice.get_unchecked(offset + 1),
208 r: slice.get_unchecked(offset + 2),
209 a: slice.get_unchecked(offset + 3),
210 }
211 }
212 // RGB8 is the same as BGR8, the order is just different.
213 //
214 Rs2Format::Rgb8 => {
215 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
216 let offset = (row * stride_in_bytes) + (col * 3);
217
218 PixelKind::Bgr8 {
219 r: slice.get_unchecked(offset),
220 g: slice.get_unchecked(offset + 1),
221 b: slice.get_unchecked(offset + 2),
222 }
223 }
224 // RGBA8 is the same as BGRA8, the order is just different.
225 //
226 Rs2Format::Rgba8 => {
227 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
228 let offset = (row * stride_in_bytes) + (col * 4);
229
230 PixelKind::Bgra8 {
231 r: slice.get_unchecked(offset),
232 g: slice.get_unchecked(offset + 1),
233 b: slice.get_unchecked(offset + 2),
234 a: slice.get_unchecked(offset + 3),
235 }
236 }
237 Rs2Format::Raw8 => {
238 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
239 let offset = (row * stride_in_bytes) + col;
240
241 PixelKind::Raw8 {
242 val: slice.get_unchecked(offset),
243 }
244 }
245 Rs2Format::Y8 => {
246 let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
247 let offset = (row * stride_in_bytes) + col;
248
249 PixelKind::Y8 {
250 y: slice.get_unchecked(offset),
251 }
252 }
253 Rs2Format::Y16 => {
254 let size = data_size_in_bytes / std::mem::size_of::<u16>();
255 let stride = stride_in_bytes / std::mem::size_of::<u16>();
256 let slice = slice::from_raw_parts(data.cast::<u16>(), size);
257 let offset = (row * stride) + col;
258
259 PixelKind::Y16 {
260 y: slice.get_unchecked(offset),
261 }
262 }
263 Rs2Format::Z16 => {
264 let size = data_size_in_bytes / std::mem::size_of::<u16>();
265 let stride = stride_in_bytes / std::mem::size_of::<u16>();
266 let slice = slice::from_raw_parts(data.cast::<u16>(), size);
267 let offset = (row * stride) + col;
268
269 PixelKind::Z16 {
270 depth: slice.get_unchecked(offset),
271 }
272 }
273 Rs2Format::Distance => {
274 let size = data_size_in_bytes / std::mem::size_of::<f32>();
275 let stride = stride_in_bytes / std::mem::size_of::<f32>();
276 let slice = slice::from_raw_parts(data.cast::<f32>(), size);
277 let offset = (row * stride) + col;
278
279 PixelKind::Distance {
280 distance: slice.get_unchecked(offset),
281 }
282 }
283 Rs2Format::Disparity32 => {
284 let size = data_size_in_bytes / std::mem::size_of::<f32>();
285 let stride = stride_in_bytes / std::mem::size_of::<f32>();
286 let slice = slice::from_raw_parts(data.cast::<f32>(), size);
287 let offset = (row * stride) + col;
288
289 PixelKind::Disparity32 {
290 disparity: slice.get_unchecked(offset),
291 }
292 }
293 Rs2Format::Xyz32F => {
294 let size = data_size_in_bytes / std::mem::size_of::<f32>();
295 let stride = stride_in_bytes / std::mem::size_of::<f32>();
296 let slice = slice::from_raw_parts(data.cast::<f32>(), size);
297 let offset = (row * stride) + col;
298
299 PixelKind::Xyz32f {
300 x: slice.get_unchecked(offset),
301 y: slice.get_unchecked(offset + 1),
302 z: slice.get_unchecked(offset + 2),
303 }
304 }
305 _ => {
306 panic!("Unsupported video format.");
307 }
308 }
309}