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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//! Type for representing the various pixel formats.
//!
//! Correct formats are determined by the device type and the streaming profile.
//! For detailed pixel format information, check the
//! [Intel RealSense SDK code](https://github.com/IntelRealSense/librealsense/blob/4f37f2ef0874c1716bce223b20e46d00532ffb04/wrappers/nodejs/index.js#L3865).

use crate::kind::Rs2Format;
use std::{os::raw::c_void, slice};

/// Type for representing the various pixel formats.
#[derive(Debug)]
pub enum PixelKind<'a> {
    /// 32-bit `y0, u, y1, v` data for every two pixels.
    /// Similar to YUV422 but packed in a different order - see [this link](https://en.wikipedia.org/wiki/YUV).
    Yuyv { y: &'a u8, u: &'a u8, v: &'a u8 },
    /// Similar to the standard YUYV pixel format, but packed in a different order.
    Uyvy { y: &'a u8, u: &'a u8, v: &'a u8 },
    /// 8-bit blue, green, and red channels -- suitable for OpenCV.
    Bgr8 { b: &'a u8, g: &'a u8, r: &'a u8 },
    /// 8-bit blue, green, and red channels + constant alpha channel equal to FF.
    Bgra8 {
        b: &'a u8,
        g: &'a u8,
        r: &'a u8,
        a: &'a u8,
    },
    /// 8-bit red, green and blue channels.
    Rgb8 { r: &'a u8, g: &'a u8, b: &'a u8 },
    /// 8-bit red, green and blue channels + constant alpha channel equal to FF.
    Rgba8 {
        r: &'a u8,
        g: &'a u8,
        b: &'a u8,
        a: &'a u8,
    },
    /// 8-bit raw image.
    Raw8 { val: &'a u8 },
    /// 8-bit per-pixel grayscale image.
    Y8 { y: &'a u8 },
    /// 16-bit per-pixel grayscale image.
    Y16 { y: &'a u16 },
    /// 16-bit linear depth values. The depth is meters is equal to depth scale * pixel value.
    Z16 { depth: &'a u16 },
    /// 32-bit float-point depth distance value.
    Distance { distance: &'a f32 },
    /// 32-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth.
    Disparity32 { disparity: &'a f32 },
    /// 32-bit floating point 3D coordinates.
    Xyz32f { x: &'a f32, y: &'a f32, z: &'a f32 },
}

/// Method to retrieve a pixel from a given rs2_frame in the requested Pixel format.
///
/// # Safety
///
/// This method should only be called from the ImageFrame types themselves, as this
/// is the only place where proper pointer management happens.
#[inline]
pub(crate) unsafe fn get_pixel<'a>(
    format: Rs2Format,
    data_size_in_bytes: usize,
    data: *const c_void,
    stride_in_bytes: usize,
    col: usize,
    row: usize,
) -> PixelKind<'a> {
    // Realsense stores frame data in row-major format. Normally, we would offset into a
    // uniform array in column major format with the following equation:
    //
    // offset = row * width + column
    //
    // The assumption here being that it is a uniform array. See individual comments below for
    // how each offset equation differs.
    //
    // NOTE; You _could_ still represent this same pointer arithmetic in row-major form, but be
    // warned that the equations will look fairly different.
    //
    match format {
        // YUYV is not uniform since it encapsulates two pixels over 32 bits (four u8
        // values). Instead, we can index YUYV (and UYVY) as follows:
        //
        // offset = (row * stride) + (col / 2) * 4
        //
        // The strange part here is the (col / 2) * 4. This is done because on odd rows we
        // don't want to offset to the next Y value, but rather take the full YUYV and pick
        // the correct Y depending on whether the row is even or odd.
        //
        // NOTE: Order matters because we are taking advantage of integer division here.
        //
        Rs2Format::Yuyv => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col / 2) * 4;

            let y = if row % 2 == 0 {
                slice.get_unchecked(offset)
            } else {
                slice.get_unchecked(offset + 2)
            };

            PixelKind::Yuyv {
                y,
                u: slice.get_unchecked(offset + 1),
                v: slice.get_unchecked(offset + 3),
            }
        }
        // UYVY follows from the same exact pattern we use for YUYV, since it's more or less a
        // re-ordering of the underlying data.
        //
        Rs2Format::Uyvy => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col / 2) * 4;

            let y = if row % 2 == 0 {
                slice.get_unchecked(offset + 1)
            } else {
                slice.get_unchecked(offset + 3)
            };

            PixelKind::Uyvy {
                y,
                u: slice.get_unchecked(offset),
                v: slice.get_unchecked(offset + 2),
            }
        }
        // For BGR / RGB, we do a similar trick, but since pixels aren't interleaved as they
        // are with YUYV / UYVY, the multipliers for column and row offsets can be uniform.
        //
        Rs2Format::Bgr8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col * 3);

            PixelKind::Bgr8 {
                b: slice.get_unchecked(offset),
                g: slice.get_unchecked(offset + 1),
                r: slice.get_unchecked(offset + 2),
            }
        }
        // BGRA8 is more or less the same as BGR8, except we use 4 as a multiplier.
        //
        Rs2Format::Bgra8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col * 4);

            PixelKind::Bgra8 {
                b: slice.get_unchecked(offset),
                g: slice.get_unchecked(offset + 1),
                r: slice.get_unchecked(offset + 2),
                a: slice.get_unchecked(offset + 3),
            }
        }
        // RGB8 is the same as BGR8, the order is just different.
        //
        Rs2Format::Rgb8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col * 3);

            PixelKind::Bgr8 {
                r: slice.get_unchecked(offset),
                g: slice.get_unchecked(offset + 1),
                b: slice.get_unchecked(offset + 2),
            }
        }
        // RGBA8 is the same as BGRA8, the order is just different.
        //
        Rs2Format::Rgba8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + (col * 4);

            PixelKind::Bgra8 {
                r: slice.get_unchecked(offset),
                g: slice.get_unchecked(offset + 1),
                b: slice.get_unchecked(offset + 2),
                a: slice.get_unchecked(offset + 3),
            }
        }
        Rs2Format::Raw8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + col;

            PixelKind::Raw8 {
                val: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Y8 => {
            let slice = slice::from_raw_parts(data.cast::<u8>(), data_size_in_bytes);
            let offset = (row * stride_in_bytes) + col;

            PixelKind::Y8 {
                y: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Y16 => {
            let size = data_size_in_bytes / std::mem::size_of::<u16>();
            let stride = stride_in_bytes / std::mem::size_of::<u16>();
            let slice = slice::from_raw_parts(data.cast::<u16>(), size);
            let offset = (row * stride) + col;

            PixelKind::Y16 {
                y: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Z16 => {
            let size = data_size_in_bytes / std::mem::size_of::<u16>();
            let stride = stride_in_bytes / std::mem::size_of::<u16>();
            let slice = slice::from_raw_parts(data.cast::<u16>(), size);
            let offset = (row * stride) + col;

            PixelKind::Z16 {
                depth: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Distance => {
            let size = data_size_in_bytes / std::mem::size_of::<f32>();
            let stride = stride_in_bytes / std::mem::size_of::<f32>();
            let slice = slice::from_raw_parts(data.cast::<f32>(), size);
            let offset = (row * stride) + col;

            PixelKind::Distance {
                distance: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Disparity32 => {
            let size = data_size_in_bytes / std::mem::size_of::<f32>();
            let stride = stride_in_bytes / std::mem::size_of::<f32>();
            let slice = slice::from_raw_parts(data.cast::<f32>(), size);
            let offset = (row * stride) + col;

            PixelKind::Disparity32 {
                disparity: slice.get_unchecked(offset),
            }
        }
        Rs2Format::Xyz32F => {
            let size = data_size_in_bytes / std::mem::size_of::<f32>();
            let stride = stride_in_bytes / std::mem::size_of::<f32>();
            let slice = slice::from_raw_parts(data.cast::<f32>(), size);
            let offset = (row * stride) + col;

            PixelKind::Xyz32f {
                x: slice.get_unchecked(offset),
                y: slice.get_unchecked(offset + 1),
                z: slice.get_unchecked(offset + 2),
            }
        }
        _ => {
            panic!("Unsupported video format.");
        }
    }
}