Skip to content

feat: Add Point Cloud Filter and 3D People Tracking Verification - #9

Open
lucumango wants to merge 1 commit into
mwcore-devfrom
feat/3d-tracking-filter
Open

feat: Add Point Cloud Filter and 3D People Tracking Verification#9
lucumango wants to merge 1 commit into
mwcore-devfrom
feat/3d-tracking-filter

Conversation

@lucumango

Copy link
Copy Markdown
Contributor

Description

This PR integrates a Statistical Outlier Removal (SOR) point cloud filter into base.py to reduce radar noise prior to tracking.
Additionally, it adds a dedicated pytest suite simulating a 3D human torso trajectory, proving Asterios can successfully track the silhouette.

Resolves #8

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to improve 3D tracking stability by adding an optional Statistical Outlier Removal (SOR) filter in the point-cloud preprocessing path and introducing a pytest-based verification that simulates tracking a moving 3D torso trajectory.

Changes:

  • Added optional SOR outlier filtering to BaseTracker.normalize_data() to remove noisy radar points before downstream processing.
  • Added a new synthetic 3D torso tracking test intended to validate Asterios tracking behavior with noisy point clouds.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
mwcore/tracking/api/base.py Extends normalize_data() with optional SOR filtering and updates input unpacking behavior.
tests/test_3d_tracking.py Adds a synthetic-trajectory pytest intended to validate filtering and tracking behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread tests/test_3d_tracking.py
Comment on lines +67 to +90
for i, center in enumerate(trajectory):
# Generate synthetic frame with noise
frame_points = generate_synthetic_torso_frame(center)

# We manually monkey-patch filter_outliers directly through the consume dictionary if kwargs allow,
# but since consume doesn't explicitly pass kwargs to normalize_data yet, we'll patch the instance method
# for testing purposes or assert behavior directly.
# Let's call normalize_data separately to verify SOR logic.

# 1. Test SOR Filter mathematically
normalized_with_filter = tracker.normalize_data(point_array=frame_points, filter_outliers=True, sor_neighbors=5, sor_std_ratio=1.0)
normalized_without_filter = tracker.normalize_data(point_array=frame_points, filter_outliers=False)

# Outliers should be dropped
assert len(normalized_with_filter) < len(normalized_without_filter), "SOR filter failed to drop outliers."

# 2. Consume data in Tracker
states = tracker.consume(point_array=normalized_with_filter)

if states:
# Get primary tracked state (Centroid)
tracked_states.append(states[0])

# Allow a few frames for the Kalman Filter to initialize and establish a Track
Comment thread tests/test_3d_tracking.py
@@ -0,0 +1,99 @@
import numpy as np
import pytest
Comment on lines 63 to +71
if det_obj is not None:
input_data = np.vstack(
(det_obj["x"], det_obj["y"], det_obj["z"], det_obj["doppler"], det_obj["peakVal"])
).T
if point_array is not None:
input_data = point_array

if filter_outliers and len(input_data) > sor_neighbors:
from scipy.spatial import cKDTree
Comment on lines 19 to +26
def normalize_data(self,
det_obj: Optional[dict] = None,
point_array: Optional[np.ndarray] = None,
keepRadial: bool = False,
transform: bool = False):
transform: bool = False,
filter_outliers: bool = False,
sor_neighbors: int = 5,
sor_std_ratio: float = 1.0):
Comment on lines 88 to 91
for index in range(len(input_data)):
x, y, z, doppler, peakVal = input_data[index]
x, y, z, doppler, peakVal = input_data[index][:5]
# Compute polar coordinates
r = math.sqrt(x**2 + y**2 + z**2)
Comment on lines +76 to +83
mean_sq_distances = np.mean(distances[:, 1:], axis=1)

global_mean_dist = np.mean(mean_sq_distances)
global_std_dist = np.std(mean_sq_distances)

# Keep points whose mean neighbor distance is within the threshold
threshold = global_mean_dist + (sor_std_ratio * global_std_dist)
mask = mean_sq_distances <= threshold
Comment thread tests/test_3d_tracking.py
Comment on lines +83 to +84
# 2. Consume data in Tracker
states = tracker.consume(point_array=normalized_with_filter)
Comment thread tests/test_3d_tracking.py
Comment on lines +5 to +24
def generate_synthetic_torso_frame(center, num_points=20, spread=0.3):
"""
Generates a noisy cluster of points representing a human torso.
Format required by tracking base: [x, y, z, doppler, peakVal]
"""
points = np.random.normal(loc=0.0, scale=spread, size=(num_points, 5))
# Apply center offset to x, y, z
points[:, 0] += center[0]
points[:, 1] += center[1]
points[:, 2] += center[2]
# Synthetic doppler and peakVal
points[:, 3] = np.random.uniform(-1.0, 1.0, size=num_points)
points[:, 4] = np.random.uniform(50, 100, size=num_points)

# Add a few extreme random noise points (outliers) to test the SOR filter
outliers = np.random.uniform(-5.0, 5.0, size=(5, 5))
outliers[:, 3] = 0.0
outliers[:, 4] = 10.0

return np.vstack((points, outliers))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add Point Cloud Filter and 3D People Tracking Verification

2 participants