oxihuman_viewer/
depth_sample.rs1#![allow(dead_code)]
4
5use std::f32::consts::FRAC_PI_4;
8
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct DepthSampleConfig {
12 pub near: f32,
13 pub far: f32,
14 pub reversed_z: bool,
15}
16
17impl Default for DepthSampleConfig {
18 fn default() -> Self {
19 Self {
20 near: 0.1,
21 far: 1000.0,
22 reversed_z: false,
23 }
24 }
25}
26
27#[allow(dead_code)]
28pub fn default_depth_sample_config() -> DepthSampleConfig {
29 DepthSampleConfig::default()
30}
31
32#[allow(dead_code)]
33pub fn ds_linearize(cfg: &DepthSampleConfig, ndc_depth: f32) -> f32 {
34 let z = ndc_depth.clamp(0.0, 1.0);
35 let z = if cfg.reversed_z { 1.0 - z } else { z };
36 let n = cfg.near;
37 let f = cfg.far;
38 (2.0 * n * f) / (f + n - z * (f - n))
39}
40
41#[allow(dead_code)]
42pub fn ds_ndc_from_linear(cfg: &DepthSampleConfig, linear_depth: f32) -> f32 {
43 let n = cfg.near;
44 let f = cfg.far;
45 let z = (2.0 * n * f / linear_depth - f - n) / (n - f);
46 let z = z.clamp(0.0, 1.0);
47 if cfg.reversed_z {
48 1.0 - z
49 } else {
50 z
51 }
52}
53
54#[allow(dead_code)]
55pub fn ds_depth_range(cfg: &DepthSampleConfig) -> f32 {
56 cfg.far - cfg.near
57}
58
59#[allow(dead_code)]
60pub fn ds_is_valid(cfg: &DepthSampleConfig, ndc_depth: f32) -> bool {
61 (0.0..=1.0).contains(&ndc_depth) && cfg.near > 0.0 && cfg.far > cfg.near
62}
63
64#[allow(dead_code)]
65pub fn ds_perspective_angle_rad(cfg: &DepthSampleConfig) -> f32 {
66 (cfg.near / cfg.far).atan().min(FRAC_PI_4)
67}
68
69#[allow(dead_code)]
70pub fn ds_sample_buffer(cfg: &DepthSampleConfig, buf: &[f32]) -> Vec<f32> {
71 buf.iter().map(|&d| ds_linearize(cfg, d)).collect()
72}
73
74#[allow(dead_code)]
75pub fn ds_average_linear(cfg: &DepthSampleConfig, buf: &[f32]) -> f32 {
76 if buf.is_empty() {
77 return 0.0;
78 }
79 let sum: f32 = buf.iter().map(|&d| ds_linearize(cfg, d)).sum();
80 sum / buf.len() as f32
81}
82
83#[allow(dead_code)]
84pub fn ds_to_json(cfg: &DepthSampleConfig) -> String {
85 format!(
86 "{{\"near\":{:.4},\"far\":{:.4},\"rev\":{}}}",
87 cfg.near, cfg.far, cfg.reversed_z
88 )
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 #[test]
95 fn default_near_far() {
96 let c = default_depth_sample_config();
97 assert!(c.near > 0.0 && c.far > c.near);
98 }
99 #[test]
100 fn linearize_zero_near() {
101 let c = default_depth_sample_config();
102 let l = ds_linearize(&c, 0.0);
103 assert!(l > 0.0);
104 }
105 #[test]
106 fn linearize_one_is_far() {
107 let c = default_depth_sample_config();
108 let l = ds_linearize(&c, 1.0);
109 assert!((l - c.far).abs() / c.far < 0.01);
110 }
111 #[test]
112 fn depth_range_correct() {
113 let c = default_depth_sample_config();
114 assert!((ds_depth_range(&c) - (c.far - c.near)).abs() < 1e-4);
115 }
116 #[test]
117 fn is_valid_mid() {
118 let c = default_depth_sample_config();
119 assert!(ds_is_valid(&c, 0.5));
120 }
121 #[test]
122 fn is_invalid_neg() {
123 let c = default_depth_sample_config();
124 assert!(!ds_is_valid(&c, -0.1));
125 }
126 #[test]
127 fn perspective_angle_nonneg() {
128 assert!(ds_perspective_angle_rad(&default_depth_sample_config()) >= 0.0);
129 }
130 #[test]
131 fn sample_buffer_same_len() {
132 let c = default_depth_sample_config();
133 let b = vec![0.2, 0.5, 0.8];
134 assert_eq!(ds_sample_buffer(&c, &b).len(), b.len());
135 }
136 #[test]
137 fn average_linear_empty_zero() {
138 assert!(ds_average_linear(&default_depth_sample_config(), &[]).abs() < 1e-6);
139 }
140 #[test]
141 fn to_json_has_near() {
142 assert!(ds_to_json(&default_depth_sample_config()).contains("\"near\""));
143 }
144}