use super::cloud::GaussianCloud;
pub struct DensifyStats {
pub grad_accum: Vec<f32>,
pub vis_count: Vec<u32>,
}
impl DensifyStats {
pub fn new(n: usize) -> Self {
Self {
grad_accum: vec![0.0; n],
vis_count: vec![0; n],
}
}
pub fn accumulate(&mut self, position_grads: &[[f32; 3]], visible: &[bool]) {
for (i, (grad, &vis)) in position_grads.iter().zip(visible.iter()).enumerate() {
if vis {
let mag = (grad[0] * grad[0] + grad[1] * grad[1] + grad[2] * grad[2]).sqrt();
self.grad_accum[i] += mag;
self.vis_count[i] += 1;
}
}
}
pub fn avg_gradients(&self) -> Vec<f32> {
self.grad_accum
.iter()
.zip(self.vis_count.iter())
.map(|(&g, &c)| if c > 0 { g / c as f32 } else { 0.0 })
.collect()
}
pub fn reset(&mut self) {
self.grad_accum.fill(0.0);
self.vis_count.fill(0);
}
}
pub struct DensifyConfig {
pub grad_threshold: f32,
pub scale_threshold: f32,
pub opacity_threshold: f32,
}
impl Default for DensifyConfig {
fn default() -> Self {
Self {
grad_threshold: 0.001,
scale_threshold: 0.01, opacity_threshold: 0.005,
}
}
}
pub fn densify(
cloud: &GaussianCloud,
stats: &DensifyStats,
config: &DensifyConfig,
) -> GaussianCloud {
let avg_grads = stats.avg_gradients();
let sh_per_g = cloud.sh_coeffs_per_gaussian();
let mut new_positions = Vec::new();
let mut new_scales = Vec::new();
let mut new_rotations = Vec::new();
let mut new_opacities = Vec::new();
let mut new_sh = Vec::new();
for i in 0..cloud.count {
let opacity = 1.0 / (1.0 + (-cloud.opacities[i]).exp()); let scale_mag = cloud.scales[i].iter().map(|s| s.exp()).sum::<f32>() / 3.0;
if opacity < config.opacity_threshold {
continue;
}
let mut keep = |idx: usize| {
new_positions.push(cloud.positions[idx]);
new_scales.push(cloud.scales[idx]);
new_rotations.push(cloud.rotations[idx]);
new_opacities.push(cloud.opacities[idx]);
let sh_start = idx * sh_per_g;
new_sh.extend_from_slice(&cloud.sh_coeffs[sh_start..sh_start + sh_per_g]);
};
if avg_grads[i] > config.grad_threshold {
if scale_mag > config.scale_threshold {
let new_scale = [
cloud.scales[i][0] - 1.6_f32.ln(),
cloud.scales[i][1] - 1.6_f32.ln(),
cloud.scales[i][2] - 1.6_f32.ln(),
];
new_positions.push(cloud.positions[i]);
new_scales.push(new_scale);
new_rotations.push(cloud.rotations[i]);
new_opacities.push(cloud.opacities[i]);
let sh_start = i * sh_per_g;
new_sh.extend_from_slice(&cloud.sh_coeffs[sh_start..sh_start + sh_per_g]);
let mut pos2 = cloud.positions[i];
pos2[0] += scale_mag * 0.5;
new_positions.push(pos2);
new_scales.push(new_scale);
new_rotations.push(cloud.rotations[i]);
new_opacities.push(cloud.opacities[i]);
new_sh.extend_from_slice(&cloud.sh_coeffs[sh_start..sh_start + sh_per_g]);
} else {
keep(i);
keep(i);
}
} else {
keep(i);
}
}
GaussianCloud {
count: new_positions.len(),
positions: new_positions,
scales: new_scales,
rotations: new_rotations,
opacities: new_opacities,
sh_coeffs: new_sh,
sh_degree: cloud.sh_degree,
}
}