orbbec_sdk/
filter.rs

1//! Filter module
2use crate::{
3    ConvertType, HoleFillMode, StreamType,
4    error::{OrbbecError, OrbbecErrorData},
5    frame::Frame,
6    stream::VideoStreamProfile,
7    sys::filter::OBFilter,
8};
9
10/// Filter trait
11pub trait Filter<F: Frame>: AsRef<OBFilter> {
12    /// Process a frame with the filter
13    fn process(&self, frame: &F) -> Result<F, OrbbecError> {
14        self.as_ref()
15            .process(frame.as_ref())
16            .map(|f| F::from(f))
17            .map_err(OrbbecError::from)
18    }
19}
20
21/// Decimation Filter
22///
23/// This filter reduces the resolution of the depth frame by an integer factor.
24pub struct DecimationFilter {
25    inner: OBFilter,
26}
27
28impl DecimationFilter {
29    /// Create a new decimation filter.
30    pub fn new() -> Result<Self, OrbbecError> {
31        // Unwrap is safe because Decimation filter is always available (public filter)
32        // Ref: https://github.com/orbbec/OrbbecSDK_v2/blob/815ae047cc977a1f7edd2b97b69ff6cd29f510b3/src/filter/publicfilters/publicFilterLoader.cpp#L32
33        let filter = OBFilter::new(c"DecimationFilter")?.unwrap();
34        Ok(DecimationFilter { inner: filter })
35    }
36
37    /// Set the decimation factor.
38    ///
39    /// ### Arguments
40    /// * `factor` - The decimation factor. Usually between 1 and 8.
41    pub fn set_factor(&mut self, factor: u8) -> Result<(), OrbbecError> {
42        self.inner
43            .set_config_value(c"decimate", factor as f64)
44            .map_err(OrbbecError::from)
45    }
46}
47
48impl AsRef<OBFilter> for DecimationFilter {
49    fn as_ref(&self) -> &OBFilter {
50        &self.inner
51    }
52}
53
54impl<F: Frame> Filter<F> for DecimationFilter {}
55
56/// Format Convert Filter
57///
58/// This filter converts the format of the input frame to a different format.
59pub struct FormatConvertFilter {
60    inner: OBFilter,
61}
62
63impl FormatConvertFilter {
64    /// Create a new format convert filter.
65    pub fn new() -> Result<Self, OrbbecError> {
66        // Unwrap is safe because FormatConvert filter is always available (public filter)
67        // Ref: https://github.com/orbbec/OrbbecSDK_v2/blob/815ae047cc977a1f7edd2b97b69ff6cd29f510b3/src/filter/publicfilters/publicFilterLoader.cpp#L32
68        let filter = OBFilter::new(c"FormatConverter")?.unwrap();
69        Ok(FormatConvertFilter { inner: filter })
70    }
71
72    /// Set the format conversion type
73    /// ### Arguments
74    /// * `convert_type` - The desired format conversion type.
75    pub fn set_convert_type(&mut self, convert_type: ConvertType) -> Result<(), OrbbecError> {
76        self.inner
77            .set_config_value(c"convertType", convert_type as u32 as f64)
78            .map_err(OrbbecError::from)
79    }
80}
81
82impl AsRef<OBFilter> for FormatConvertFilter {
83    fn as_ref(&self) -> &OBFilter {
84        &self.inner
85    }
86}
87
88impl<F: Frame> Filter<F> for FormatConvertFilter {}
89
90/// Hole Filling Filter
91///
92/// This filter fills holes in the depth frame using a specified hole filling mode.
93pub struct HoleFillingFilter {
94    inner: OBFilter,
95}
96
97impl HoleFillingFilter {
98    /// Create a new hole filling filter.
99    pub fn new() -> Result<Self, OrbbecError> {
100        match OBFilter::new(c"HoleFillingFilter")? {
101            Some(f) => Ok(HoleFillingFilter { inner: f }),
102            None => {
103                let err_data = OrbbecErrorData {
104                    message: "HoleFillingFilter is not available".to_string(),
105                    function: "HoleFillingFilter::new".to_string(),
106                    args: "".to_string(),
107                };
108
109                Err(OrbbecError::NotImplemented(err_data))
110            }
111        }
112    }
113
114    /// Set the hole filling mode.
115    ///
116    /// ### Arguments
117    /// * `mode` - The hole filling mode.
118    pub fn set_mode(&mut self, mode: HoleFillMode) -> Result<(), OrbbecError> {
119        self.inner
120            .set_config_value(c"hole_filling_mode", mode as u32 as f64)
121            .map_err(OrbbecError::from)
122    }
123}
124
125impl AsRef<OBFilter> for HoleFillingFilter {
126    fn as_ref(&self) -> &OBFilter {
127        &self.inner
128    }
129}
130
131impl<F: Frame> Filter<F> for HoleFillingFilter {}
132
133/// Temporal Filter
134///
135/// This filter smooths the depth frame over time to reduce noise and temporal artifacts.
136pub struct TemporalFilter {
137    inner: OBFilter,
138}
139
140impl TemporalFilter {
141    /// Create a new temporal filter.
142    pub fn new() -> Result<Self, OrbbecError> {
143        match OBFilter::new(c"TemporalFilter")? {
144            Some(f) => Ok(TemporalFilter { inner: f }),
145            None => {
146                let err_data = OrbbecErrorData {
147                    message: "TemporalFilter is not available".to_string(),
148                    function: "TemporalFilter::new".to_string(),
149                    args: "".to_string(),
150                };
151
152                Err(OrbbecError::NotImplemented(err_data))
153            }
154        }
155    }
156
157    /// Set the threshold parameter.
158    ///
159    /// Threshold is the maximum change (in percentage) for a pixel to still be considered static (without motion).
160    /// ### Arguments
161    /// * `threshold` - The threshold value. Usually between 0.1 and 1.0.
162    pub fn set_threshold(&mut self, threshold: f32) -> Result<(), OrbbecError> {
163        self.inner
164            .set_config_value(c"diff_scale", threshold as f64)
165            .map_err(OrbbecError::from)
166    }
167
168    /// Set the weight parameter.
169    ///
170    /// Weight is how much the current frame affects the output frame.
171    /// ### Arguments
172    /// * `weight` - The weight value. Usually between 0.1 and 1.0.
173    pub fn set_weight(&mut self, weight: f32) -> Result<(), OrbbecError> {
174        self.inner
175            .set_config_value(c"weight", weight as f64)
176            .map_err(OrbbecError::from)
177    }
178}
179
180impl AsRef<OBFilter> for TemporalFilter {
181    fn as_ref(&self) -> &OBFilter {
182        &self.inner
183    }
184}
185
186impl<F: Frame> Filter<F> for TemporalFilter {}
187
188/// Spatial Fast Filter
189///
190/// This filter smooths the depth frame while preserving edges.
191/// It uses an enhanced median smoothing algorithm and is the simplest spatial filter available.
192pub struct SpatialFastFilter {
193    inner: OBFilter,
194}
195
196impl SpatialFastFilter {
197    /// Create a new spatial fast filter.
198    pub fn new() -> Result<Self, OrbbecError> {
199        match OBFilter::new(c"SpatialFastFilter")? {
200            Some(f) => Ok(SpatialFastFilter { inner: f }),
201            None => {
202                let err_data = OrbbecErrorData {
203                    message: "SpatialFastFilter is not available".to_string(),
204                    function: "SpatialFastFilter::new".to_string(),
205                    args: "".to_string(),
206                };
207
208                Err(OrbbecError::NotImplemented(err_data))
209            }
210        }
211    }
212
213    /// Set the filter radius parameter.
214    ///
215    /// Radius is how many neighbors will be considered for smoothing.
216    /// ### Arguments
217    /// * `radius` - The radius value. Usually between 3 and 5.
218    pub fn set_radius(&mut self, radius: u16) -> Result<(), OrbbecError> {
219        self.inner
220            .set_config_value(c"radius", radius as f64)
221            .map_err(OrbbecError::from)
222    }
223}
224
225impl AsRef<OBFilter> for SpatialFastFilter {
226    fn as_ref(&self) -> &OBFilter {
227        &self.inner
228    }
229}
230
231impl<F: Frame> Filter<F> for SpatialFastFilter {}
232
233/// Spatial Moderate Filter
234///
235/// This filter smooths the depth frame while preserving edges.
236/// It uses an optimized average smoothing algorithm and is a balance between speed and quality.
237pub struct SpatialModerateFilter {
238    inner: OBFilter,
239}
240
241impl SpatialModerateFilter {
242    /// Create a new spatial moderate filter.
243    pub fn new() -> Result<Self, OrbbecError> {
244        match OBFilter::new(c"SpatialModerateFilter")? {
245            Some(f) => Ok(SpatialModerateFilter { inner: f }),
246            None => {
247                let err_data = OrbbecErrorData {
248                    message: "SpatialModerateFilter is not available".to_string(),
249                    function: "SpatialModerateFilter::new".to_string(),
250                    args: "".to_string(),
251                };
252
253                Err(OrbbecError::NotImplemented(err_data))
254            }
255        }
256    }
257
258    /// Set the filter radius parameter.
259    ///
260    /// Radius is how many neighbors will be considered for smoothing.
261    /// ### Arguments
262    /// * `radius` - The radius value. Usually between 3 and 7.
263    pub fn set_radius(&mut self, radius: u16) -> Result<(), OrbbecError> {
264        self.inner
265            .set_config_value(c"radius", radius as f64)
266            .map_err(OrbbecError::from)
267    }
268
269    /// Set the filter magnitude parameter.
270    ///
271    /// Magnitude is the strength of the filter.
272    /// ### Arguments
273    /// * `magnitude` - The magnitude value. Usually between 1 and 3.
274    pub fn set_magnitude(&mut self, magnitude: u8) -> Result<(), OrbbecError> {
275        self.inner
276            .set_config_value(c"magnitude", magnitude as f64)
277            .map_err(OrbbecError::from)
278    }
279
280    /// Set the filter threshold parameter.
281    ///
282    /// Threshold is the maximum difference between pixels to be considered part of the same surface.
283    /// ### Arguments
284    /// * `threshold` - The threshold value. Usually between 1 and 10000.
285    pub fn set_threshold(&mut self, threshold: u16) -> Result<(), OrbbecError> {
286        self.inner
287            .set_config_value(c"disp_diff", threshold as f64)
288            .map_err(OrbbecError::from)
289    }
290}
291
292impl AsRef<OBFilter> for SpatialModerateFilter {
293    fn as_ref(&self) -> &OBFilter {
294        &self.inner
295    }
296}
297
298impl<F: Frame> Filter<F> for SpatialModerateFilter {}
299
300///  Spatial Advanced Filter
301///
302/// This filter smooths the depth frame while preserving edges.
303/// It uses a more advanced algorithm with more parameters to tune.
304pub struct SpatialAdvancedFilter {
305    inner: OBFilter,
306}
307
308impl SpatialAdvancedFilter {
309    /// Create a new spatial advanced filter.
310    pub fn new() -> Result<Self, OrbbecError> {
311        match OBFilter::new(c"SpatialAdvancedFilter")? {
312            Some(f) => Ok(SpatialAdvancedFilter { inner: f }),
313            None => {
314                let err_data = OrbbecErrorData {
315                    message: "SpatialAdvancedFilter is not available".to_string(),
316                    function: "SpatialAdvancedFilter::new".to_string(),
317                    args: "".to_string(),
318                };
319
320                Err(OrbbecError::NotImplemented(err_data))
321            }
322        }
323    }
324
325    /// Set the filter alpha parameter.
326    ///
327    /// Alpha is the weight of a pixel vs neighbors of the same surface.
328    /// ### Arguments
329    /// * `alpha` - The alpha value. Usually between 0.1 and 1.0.
330    pub fn set_alpha(&mut self, alpha: f32) -> Result<(), OrbbecError> {
331        self.inner
332            .set_config_value(c"alpha", alpha as f64)
333            .map_err(OrbbecError::from)
334    }
335
336    /// Set the filter threshold parameter.
337    ///
338    /// Threshold is the maximum difference between pixels to be considered part of the same surface.
339    /// ### Arguments
340    /// * `threshold` - The threshold value. Usually between 1 and 10000.
341    pub fn set_threshold(&mut self, threshold: u16) -> Result<(), OrbbecError> {
342        self.inner
343            .set_config_value(c"disp_diff", threshold as f64)
344            .map_err(OrbbecError::from)
345    }
346
347    /// Set the filter radius parameter.
348    ///
349    /// Radius is how many missing pixels will be filled in.
350    /// ### Arguments
351    /// * `radius` - The radius value. Usually between 0 and 8.
352    pub fn set_radius(&mut self, radius: u16) -> Result<(), OrbbecError> {
353        self.inner
354            .set_config_value(c"radius", radius as f64)
355            .map_err(OrbbecError::from)
356    }
357
358    /// Set the filter magnitude parameter.
359    ///
360    /// Magnitude is the strength of the filter.
361    /// ### Arguments
362    /// * `magnitude` - The magnitude value. Usually between 1 and 5.
363    pub fn set_magnitude(&mut self, magnitude: u8) -> Result<(), OrbbecError> {
364        self.inner
365            .set_config_value(c"magnitude", magnitude as f64)
366            .map_err(OrbbecError::from)
367    }
368}
369
370impl AsRef<OBFilter> for SpatialAdvancedFilter {
371    fn as_ref(&self) -> &OBFilter {
372        &self.inner
373    }
374}
375
376impl<F: Frame> Filter<F> for SpatialAdvancedFilter {}
377
378/// Threshold filter
379///
380/// This filter removes depth values outside the specified range.
381pub struct ThresholdFilter {
382    inner: OBFilter,
383}
384
385impl ThresholdFilter {
386    /// Create a new threshold filter.
387    pub fn new() -> Result<Self, OrbbecError> {
388        // Unwrap is safe because Threshold filter is always available (public filter)
389        // Ref: https://github.com/orbbec/OrbbecSDK_v2/blob/815ae047cc977a1f7edd2b97b69ff6cd29f510b3/src/filter/publicfilters/publicFilterLoader.cpp#L32
390        let filter = OBFilter::new(c"ThresholdFilter")?.unwrap();
391        Ok(ThresholdFilter { inner: filter })
392    }
393
394    /// Set the minimum depth value (in millimeters).
395    /// ### Arguments
396    /// * `min_depth` - The minimum depth value to keep, must be between 0 and 16000.
397    pub fn set_min_depth(&mut self, min_depth: u16) -> Result<(), OrbbecError> {
398        self.inner
399            .set_config_value(c"min", min_depth as f64)
400            .map_err(OrbbecError::from)
401    }
402
403    /// Set the maximum depth value (in millimeters).
404    /// ### Arguments
405    /// * `max_depth` - The maximum depth value to keep, must be between 0 and 16000.
406    pub fn set_max_depth(&mut self, max_depth: u16) -> Result<(), OrbbecError> {
407        self.inner
408            .set_config_value(c"max", max_depth as f64)
409            .map_err(OrbbecError::from)
410    }
411}
412
413impl AsRef<OBFilter> for ThresholdFilter {
414    fn as_ref(&self) -> &OBFilter {
415        &self.inner
416    }
417}
418
419impl<F: Frame> Filter<F> for ThresholdFilter {}
420
421/// Align filter
422///
423/// This filter aligns a stream (usually depth) to another stream (usually color).
424pub struct AlignFilter {
425    inner: OBFilter,
426}
427
428impl AlignFilter {
429    /// Create a new align filter.
430    /// By default, it aligns to the color stream and resizes the aligned frame to match the target stream resolution.
431    pub fn new() -> Result<Self, OrbbecError> {
432        // Unwrap is safe because Align filter is always available (public filter)
433        // Ref: https://github.com/orbbec/OrbbecSDK_v2/blob/815ae047cc977a1f7edd2b97b69ff6cd29f510b3/src/filter/publicfilters/publicFilterLoader.cpp#L32
434        let filter = OBFilter::new(c"Align")?.unwrap();
435        Ok(AlignFilter { inner: filter })
436    }
437
438    /// Align to the specified stream type.
439    /// ### Arguments
440    /// * `stream_type` - The stream type to align to.
441    pub fn set_align_to_stream_type(&mut self, stream_type: StreamType) -> Result<(), OrbbecError> {
442        self.inner
443            .set_config_value(c"AlignType", stream_type as u32 as f64)
444            .map_err(OrbbecError::from)
445    }
446
447    /// Add target stream distortion to the aligned frame.
448    /// ### Arguments
449    /// * `enable` - Whether to enable distortion.
450    pub fn set_add_distortion(&mut self, enable: bool) -> Result<(), OrbbecError> {
451        self.inner
452            .set_config_value(c"TargetDistortion", if enable { 1.0 } else { 0.0 })
453            .map_err(OrbbecError::from)
454    }
455
456    /// Set gap fill mode.
457    /// ### Arguments
458    /// * `enable` - Whether to enable gap fill.
459    pub fn set_gap_fill(&mut self, enable: bool) -> Result<(), OrbbecError> {
460        self.inner
461            .set_config_value(c"GapFillCopy", if enable { 1.0 } else { 0.0 })
462            .map_err(OrbbecError::from)
463    }
464
465    /// Set match resolution mode. When enabled, the aligned frame will have the same resolution as the target stream.
466    /// ### Arguments
467    /// * `enable` - Whether to enable match resolution.
468    pub fn set_match_resolution(&mut self, enable: bool) -> Result<(), OrbbecError> {
469        self.inner
470            .set_config_value(c"MatchTargetRes", if enable { 1.0 } else { 0.0 })
471            .map_err(OrbbecError::from)
472    }
473
474    /// Align to the specified stream profile.
475    /// It is useful when the align target stream has not started (without any frame to get intrinsics and extrinsics).
476    /// ### Arguments
477    /// * `profile` - The video stream profile to align to.
478    pub fn set_align_to_stream_profile(
479        &mut self,
480        profile: &VideoStreamProfile,
481    ) -> Result<(), OrbbecError> {
482        self.inner
483            .set_align_to(profile.inner())
484            .map_err(OrbbecError::from)
485    }
486}
487
488impl AsRef<OBFilter> for AlignFilter {
489    fn as_ref(&self) -> &OBFilter {
490        &self.inner
491    }
492}
493
494impl<F: Frame> Filter<F> for AlignFilter {}