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}