Files
ruvnet--RuView/v2/crates/wifi-densepose-pointcloud/src/pointcloud.rs
T
ruv 2754af804e feat(occworld): real conv encoder/decoder forward pass + honesty flag
Replace the `Tensor::randn` stubs in occworld-candle's VQVAE encoder
(`encode_occupancy`) and decoder (`decode_to_logits`) with a real,
deterministic, input-dependent convolutional forward pass. Previously
`predict()` emitted trajectory waypoints + confidence that were a function
of RANDOM NOISE, independent of the input and silently presented as model
output — the exact "AI slop" the project must eliminate.

occworld-candle:
- New `cnn.rs`: `Encoder2D` (3× Conv2d + GELU, interpolate2d to pin the
  token grid) and `Decoder2D` (upsample_nearest2d + Conv2d + 1×1 head).
  Both are deterministic functions of the input — same input → identical
  output; different input → different output. No randn in any forward path.
- Deterministic weight init (`det_fill`, seeded xorshift64*) across all
  `dummy()` constructors (encoder/decoder, VQ codebook, quant-convs,
  transformer), so untrained engines are bit-for-bit reproducible.
- `InferenceOutput.weights_trained: bool` — honest disclosure flag. `false`
  for `dummy()` (real but untrained net), `true` only after `load()` reads a
  real checkpoint. Priors are always from the real forward pass, never faked.
- VQ codebook + quant/post-quant convs kept and wired encoder→VQ→decoder.
- Centerpiece tests in `tests/predict_honesty.rs` (input-dependence,
  run-to-run + cross-engine determinism, untrained flag). All three FAIL on
  the old randn stub (verified by temporarily reinstating randn).

pointcloud:
- Optimize `to_gaussian_splats` hot path: 9 separate `.iter().sum()` passes
  per voxel → 2 fused accumulation passes. Bit-identical output.
- `benches/splats_bench.rs` (criterion) measures old 9-pass vs new 2-pass
  with a parity guard. ~1.3× faster on representative cloud sizes.
- Confirmed: no `randn`/placeholder in any claimed production path. The
  remaining synthetic generators (`send_test_frames`, `demo_depth_cloud`)
  and honestly-flagged heuristics (`heuristic_pose_from_amplitude`,
  luminance pseudo-depth fallback) are explicitly disclosed, not faked output.

DATA-GATED: a trained checkpoint. An untrained-but-real net is the honest
deliverable; accuracy is flagged via `weights_trained`, never claimed.

Tests: occworld 16 unit + 3 integration + 2 doc, pointcloud 18 — all pass
(CPU `Device::Cpu`; CUDA feature is GPU-gated and untouched).

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-06-11 21:47:19 -04:00

210 lines
6.5 KiB
Rust

//! Point cloud types + PLY export + Gaussian splat conversion.
#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use std::io::Write;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Point3D {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ColorPoint {
pub x: f32,
pub y: f32,
pub z: f32,
pub r: u8,
pub g: u8,
pub b: u8,
pub intensity: f32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PointCloud {
pub points: Vec<ColorPoint>,
pub timestamp_ms: i64,
pub source: String,
}
impl PointCloud {
pub fn new(source: &str) -> Self {
Self {
points: Vec::new(),
timestamp_ms: chrono::Utc::now().timestamp_millis(),
source: source.to_string(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn add(&mut self, x: f32, y: f32, z: f32, r: u8, g: u8, b: u8, intensity: f32) {
self.points.push(ColorPoint {
x,
y,
z,
r,
g,
b,
intensity,
});
}
pub fn bounds(&self) -> ([f32; 3], [f32; 3]) {
if self.points.is_empty() {
return ([0.0; 3], [0.0; 3]);
}
let mut min = [f32::MAX; 3];
let mut max = [f32::MIN; 3];
for p in &self.points {
min[0] = min[0].min(p.x);
min[1] = min[1].min(p.y);
min[2] = min[2].min(p.z);
max[0] = max[0].max(p.x);
max[1] = max[1].max(p.y);
max[2] = max[2].max(p.z);
}
(min, max)
}
}
/// Write point cloud to PLY format (ASCII).
pub fn write_ply(cloud: &PointCloud, path: &str) -> anyhow::Result<()> {
let mut f = std::fs::File::create(path)?;
writeln!(f, "ply")?;
writeln!(f, "format ascii 1.0")?;
writeln!(f, "comment Generated by RuView Dense Point Cloud")?;
writeln!(f, "comment Source: {}", cloud.source)?;
writeln!(f, "comment Timestamp: {}", cloud.timestamp_ms)?;
writeln!(f, "element vertex {}", cloud.points.len())?;
writeln!(f, "property float x")?;
writeln!(f, "property float y")?;
writeln!(f, "property float z")?;
writeln!(f, "property uchar red")?;
writeln!(f, "property uchar green")?;
writeln!(f, "property uchar blue")?;
writeln!(f, "property float intensity")?;
writeln!(f, "end_header")?;
for p in &cloud.points {
writeln!(
f,
"{:.4} {:.4} {:.4} {} {} {} {:.4}",
p.x, p.y, p.z, p.r, p.g, p.b, p.intensity
)?;
}
Ok(())
}
/// Convert point cloud to Gaussian splats for 3D rendering.
#[derive(Serialize, Deserialize)]
pub struct GaussianSplat {
pub center: [f32; 3],
pub color: [f32; 3],
pub opacity: f32,
pub scale: [f32; 3],
}
pub fn to_gaussian_splats(cloud: &PointCloud) -> Vec<GaussianSplat> {
// Cluster points into voxels and create one Gaussian per cluster
let voxel_size = 0.08; // smaller voxels = more detail = visible movement
let mut cells: std::collections::HashMap<(i32, i32, i32), Vec<&ColorPoint>> =
std::collections::HashMap::new();
for p in &cloud.points {
let key = (
(p.x / voxel_size).floor() as i32,
(p.y / voxel_size).floor() as i32,
(p.z / voxel_size).floor() as i32,
);
cells.entry(key).or_default().push(p);
}
cells
.values()
.map(|pts| {
let n = pts.len() as f32;
// Pass 1 — single fused accumulation of all six sums (position +
// colour). Replaces six separate `.iter().sum()` passes; identical
// f32 accumulation order, so the result is bit-for-bit unchanged.
let (mut sum_x, mut sum_y, mut sum_z) = (0.0f32, 0.0f32, 0.0f32);
let (mut sum_r, mut sum_g, mut sum_b) = (0.0f32, 0.0f32, 0.0f32);
for p in pts {
sum_x += p.x;
sum_y += p.y;
sum_z += p.z;
sum_r += p.r as f32;
sum_g += p.g as f32;
sum_b += p.b as f32;
}
let cx = sum_x / n;
let cy = sum_y / n;
let cz = sum_z / n;
let cr = sum_r / n / 255.0;
let cg = sum_g / n / 255.0;
let cb = sum_b / n / 255.0;
// Pass 2 — spread (mean absolute deviation) needs the centroid, so
// it is a second fused pass instead of three separate ones.
let (mut dev_x, mut dev_y, mut dev_z) = (0.0f32, 0.0f32, 0.0f32);
for p in pts {
dev_x += (p.x - cx).abs();
dev_y += (p.y - cy).abs();
dev_z += (p.z - cz).abs();
}
let sx = dev_x / n + 0.01;
let sy = dev_y / n + 0.01;
let sz = dev_z / n + 0.01;
GaussianSplat {
center: [cx, cy, cz],
color: [cr, cg, cb],
opacity: (n / 10.0).min(1.0),
scale: [sx, sy, sz],
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_cloud_has_no_splats() {
let cloud = PointCloud::new("test");
assert!(to_gaussian_splats(&cloud).is_empty());
}
#[test]
fn single_voxel_centroid_and_scale_are_correct() {
// Two points inside the same 0.08 m voxel: (0.01,0.01,0.01) and
// (0.03,0.03,0.03). Centroid = 0.02 each axis; mean-abs-dev = 0.01;
// scale = 0.01 + 0.01 = 0.02. Colours: r=0 and r=255 → mean 127.5/255.
let mut cloud = PointCloud::new("test");
cloud.add(0.01, 0.01, 0.01, 0, 0, 0, 1.0);
cloud.add(0.03, 0.03, 0.03, 255, 255, 255, 1.0);
let splats = to_gaussian_splats(&cloud);
assert_eq!(splats.len(), 1, "both points fall in one voxel");
let s = &splats[0];
for axis in 0..3 {
assert!((s.center[axis] - 0.02).abs() < 1e-5, "center[{axis}]={}", s.center[axis]);
assert!((s.scale[axis] - 0.02).abs() < 1e-5, "scale[{axis}]={}", s.scale[axis]);
assert!((s.color[axis] - 127.5 / 255.0).abs() < 1e-5, "color[{axis}]");
}
// opacity = n/10 = 0.2
assert!((s.opacity - 0.2).abs() < 1e-6);
}
#[test]
fn distinct_voxels_yield_distinct_splats() {
// Two points far apart → two separate voxels → two splats.
let mut cloud = PointCloud::new("test");
cloud.add(0.0, 0.0, 0.0, 10, 20, 30, 1.0);
cloud.add(1.0, 1.0, 1.0, 40, 50, 60, 1.0);
assert_eq!(to_gaussian_splats(&cloud).len(), 2);
}
}