diff --git a/.claude-flow/data/pending-insights.jsonl b/.claude-flow/data/pending-insights.jsonl deleted file mode 100644 index ae9d8876..00000000 --- a/.claude-flow/data/pending-insights.jsonl +++ /dev/null @@ -1,13 +0,0 @@ -{"type":"edit","file":"unknown","timestamp":1772725155061,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725341920,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725344759,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725350123,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725549376,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725716975,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725889463,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772725893374,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772726006058,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772726169252,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772726170029,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772726177792,"sessionId":null} -{"type":"edit","file":"unknown","timestamp":1772726186165,"sessionId":null} diff --git a/.claude-flow/sessions/current.json b/.claude-flow/sessions/current.json deleted file mode 100644 index ab76a4a2..00000000 --- a/.claude-flow/sessions/current.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "session-1772726138295", - "startedAt": "2026-03-05T15:55:38.296Z", - "cwd": "C:\\Users\\ruv\\Projects\\wifi-densepose", - "context": {}, - "metrics": { - "edits": 4, - "commands": 0, - "tasks": 0, - "errors": 0 - } -} \ No newline at end of file diff --git a/firmware/esp32-csi-node/components/wasm3/wasm3-src b/firmware/esp32-csi-node/components/wasm3/wasm3-src deleted file mode 160000 index 79d412ea..00000000 --- a/firmware/esp32-csi-node/components/wasm3/wasm3-src +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 79d412ea5fcf92f0efe658d52827a0e0a96ff442 diff --git a/rust-port/wifi-densepose-rs/target/debug/sensing-server.exe b/rust-port/wifi-densepose-rs/target/debug/sensing-server.exe deleted file mode 100644 index 7b0c53d8..00000000 Binary files a/rust-port/wifi-densepose-rs/target/debug/sensing-server.exe and /dev/null differ diff --git a/ui/index.html b/ui/index.html deleted file mode 100644 index a68dc799..00000000 --- a/ui/index.html +++ /dev/null @@ -1,513 +0,0 @@ - - - - - - WiFi DensePose: Human Tracking Through Walls - - - -
- -
-

WiFi DensePose

-

Human Tracking Through Walls Using WiFi Signals

-
- - - -
-
- - - - - -
-
-

Revolutionary WiFi-Based Human Pose Detection

-

- AI can track your full-body movement through walls using just WiFi signals. - Researchers at Carnegie Mellon have trained a neural network to turn basic WiFi - signals into detailed wireframe models of human bodies. -

- - - - - -
-

System Status

-
-
- API Server - - - -
-
- Hardware - - - -
-
- Inference - - - -
-
- Streaming - - - -
-
- Data Source - - - -
-
-
- - -
-

System Metrics

-
-
- CPU Usage -
-
-
- 0% -
-
- Memory Usage -
-
-
- 0% -
-
- Disk Usage -
-
-
- 0% -
-
-
- - -
-

Features

-
-
- - -
-

Live Statistics

-
-
- Active Persons - 0 -
-
- Avg Confidence - 0% -
-
- Total Detections - 0 -
-
- -
-

Zone Occupancy

-
-
-
- -
-
-
🏠
-

Through Walls

-

Works through solid barriers with no line of sight required

-
-
-
🔒
-

Privacy-Preserving

-

No cameras or visual recording - just WiFi signal analysis

-
-
-
-

Real-Time

-

Maps 24 body regions in real-time at 100Hz sampling rate

-
-
-
💰
-

Low Cost

-

Built using $30 commercial WiFi hardware

-
-
- -
-
- 24 - Body Regions -
-
- 100Hz - Sampling Rate -
-
- 87.2% - Accuracy (AP@50) -
-
- $30 - Hardware Cost -
-
-
-
- - -
-

Hardware Configuration

- -
-
-

3×3 Antenna Array

-

Click antennas to toggle their state

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Transmitters (3) -
-
-
- Receivers (6) -
-
-
-
-
- -
-

WiFi Configuration

-
-
- -
2.4GHz ± 20MHz
-
-
- -
30
-
-
- -
100 Hz
-
-
- -
$30
-
-
- -
-

Real-time CSI Data

-
-
- Amplitude: -
-
-
- 0.75 -
-
- Phase: -
-
-
- 1.2π -
-
-
-
-
-
- - -
-

Live Demonstration

- -
- - -
- Ready -
-
- -
-
-

WiFi Signal Analysis

-
- -
-
-
- Signal Strength: - -45 dBm -
-
- Processing Latency: - 12 ms -
-
-
- -
-

Human Pose Detection

-
- -
-
-
- Persons Detected: - 0 -
-
- Confidence: - 0.0% -
-
- Keypoints: - 0/0 -
-
-
-
-
- - -
-

System Architecture

- -
- WiFi DensePose Architecture - -
-
-
1
-

CSI Input

-

Channel State Information collected from WiFi antenna array

-
-
-
2
-

Phase Sanitization

-

Remove hardware-specific noise and normalize signal phase

-
-
-
3
-

Modality Translation

-

Convert WiFi signals to visual representation using CNN

-
-
-
4
-

DensePose-RCNN

-

Extract human pose keypoints and body part segmentation

-
-
-
5
-

Wireframe Output

-

Generate final human pose wireframe visualization

-
-
-
-
- - -
-

Performance Analysis

- -
- Performance Comparison Chart -
- -
-
-

WiFi-based (Same Layout)

-
-
- Average Precision: - 43.5% -
-
- AP@50: - 87.2% -
-
- AP@75: - 44.6% -
-
-
- -
-

Image-based (Reference)

-
-
- Average Precision: - 84.7% -
-
- AP@50: - 94.4% -
-
- AP@75: - 77.1% -
-
-
- -
-

Advantages & Limitations

-
-
-

Advantages

-
    -
  • Through-wall detection
  • -
  • Privacy preserving
  • -
  • Lighting independent
  • -
  • Low cost hardware
  • -
  • Uses existing WiFi
  • -
-
-
-

Limitations

-
    -
  • Performance drops in different layouts
  • -
  • Requires WiFi-compatible devices
  • -
  • Training requires synchronized data
  • -
-
-
-
-
-
- - -
-

Real-World Applications

- -
-
-
👴
-

Elderly Care Monitoring

-

Monitor elderly individuals for falls or emergencies without invading privacy. Track movement patterns and detect anomalies in daily routines.

-
- Fall Detection - Activity Monitoring - Emergency Alert -
-
- -
-
🏠
-

Home Security Systems

-

Detect intruders and monitor home security without visible cameras. Track multiple persons and identify suspicious movement patterns.

-
- Intrusion Detection - Multi-person Tracking - Invisible Monitoring -
-
- -
-
🏥
-

Healthcare Patient Monitoring

-

Monitor patients in hospitals and care facilities. Track vital signs through movement analysis and detect health emergencies.

-
- Vital Sign Analysis - Movement Tracking - Health Alerts -
-
- -
-
🏢
-

Smart Building Occupancy

-

Optimize building energy consumption by tracking occupancy patterns. Control lighting, HVAC, and security systems automatically.

-
- Energy Optimization - Occupancy Tracking - Smart Controls -
-
- -
-
🥽
-

AR/VR Applications

-

Enable full-body tracking for virtual and augmented reality applications without wearing additional sensors or cameras.

-
- Full Body Tracking - Sensor-free - Immersive Experience -
-
-
- -
-

Implementation Considerations

-

While WiFi DensePose offers revolutionary capabilities, successful implementation requires careful consideration of environment setup, data privacy regulations, and system calibration for optimal performance.

-
-
- - -
- - -
-
-

Model Training

-

Record CSI data, train pose estimation models, and manage .rvf files

-
-
-
-
-
-
-
- - -
- - - - - \ No newline at end of file diff --git a/ui/observatory/css/observatory.css b/ui/observatory/css/observatory.css deleted file mode 100644 index 225921f1..00000000 --- a/ui/observatory/css/observatory.css +++ /dev/null @@ -1,812 +0,0 @@ -/* ============================================================ - RuView Observatory — Foundation Color Scheme - Warm dark background, electric green wireframe, amber data - ============================================================ */ - -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;600&display=swap'); - -:root { - --bg-deep: #080c14; - --bg-panel: rgba(8, 16, 28, 0.85); - --bg-panel-border: rgba(0, 210, 120, 0.2); - --green-glow: #00d878; - --green-bright:#3eff8a; - --green-dim: #0a6b3a; - --amber: #ffb020; - --amber-dim: #a06800; - --blue-signal: #2090ff; - --blue-dim: #0a3060; - --red-alert: #ff3040; - --red-heart: #ff4060; - --text-primary: #e8ece0; - --text-secondary: rgba(232,236,224, 0.55); - --text-label: rgba(232,236,224, 0.4); -} - -* { margin: 0; padding: 0; box-sizing: border-box; } - -body { - background: var(--bg-deep); - overflow: hidden; - font-family: 'Inter', -apple-system, sans-serif; - color: var(--text-primary); - -webkit-font-smoothing: antialiased; -} - -#observatory-canvas { - position: fixed; - top: 0; left: 0; - width: 100vw; height: 100vh; - touch-action: none; -} - -/* ---- HUD Overlay ---- */ -#hud { - position: fixed; - top: 0; left: 0; - width: 100%; height: 100%; - pointer-events: none; - z-index: 10; -} - -/* ---- Brand ---- */ -#brand { - position: absolute; - top: 24px; left: 28px; -} - -#brand-logo { - font-family: 'Inter', sans-serif; - font-weight: 700; - font-size: 32px; - color: var(--text-primary); - letter-spacing: -0.5px; - text-shadow: 0 0 30px rgba(0, 216, 120, 0.3); -} - -.pi { - color: var(--green-glow); - font-style: italic; - margin-right: 2px; -} - -#brand-tagline { - font-size: 11px; - color: var(--text-secondary); - letter-spacing: 1.5px; - text-transform: uppercase; - margin-top: 2px; -} - -/* ---- Status bar (top right) ---- */ -#status-bar { - position: absolute; - top: 24px; right: 28px; - display: flex; - align-items: center; - gap: 12px; -} - -#data-source-badge { - display: flex; - align-items: center; - gap: 6px; - padding: 5px 12px; - border-radius: 20px; - background: rgba(0, 216, 120, 0.1); - border: 1px solid rgba(0, 216, 120, 0.25); - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - letter-spacing: 1px; - color: var(--green-glow); -} - -.dot { - width: 7px; height: 7px; - border-radius: 50%; - display: inline-block; -} -.dot--demo { background: var(--amber); box-shadow: 0 0 6px var(--amber); } -.dot--live { background: var(--green-glow); box-shadow: 0 0 6px var(--green-glow); animation: pulse-dot 2s infinite; } - -@keyframes pulse-dot { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.4; } -} - -#scenario-area { - display: flex; - align-items: center; - gap: 8px; - padding: 5px 14px; - border-radius: 20px; - background: rgba(255, 176, 32, 0.1); - border: 1px solid rgba(255, 176, 32, 0.25); - pointer-events: auto; -} -#autoplay-icon { - font-size: 10px; - color: var(--green-glow); - animation: pulse-dot 2s infinite; -} -#autoplay-icon.hidden { display: none; } -#scenario-quick-select { - background: none; - border: none; - padding: 0; - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - letter-spacing: 0.5px; - color: var(--amber); - cursor: pointer; - outline: none; -} -#scenario-quick-select:hover, -#scenario-quick-select:focus { color: var(--green-glow); } -#scenario-quick-select option { - background: #0c1420; - color: var(--text-primary); - font-family: 'JetBrains Mono', monospace; - font-size: 12px; - padding: 4px 8px; -} - -#fps-counter { - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - color: var(--text-secondary); -} - -/* ---- Data Panels ---- */ -.data-panel { - position: absolute; - width: 220px; - background: var(--bg-panel); - border: 1px solid var(--bg-panel-border); - border-radius: 12px; - padding: 16px; - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - pointer-events: auto; -} - -.panel-header { - font-family: 'JetBrains Mono', monospace; - font-size: 10px; - font-weight: 600; - letter-spacing: 2px; - text-transform: uppercase; - color: var(--text-label); - margin-bottom: 14px; - padding-bottom: 8px; - border-bottom: 1px solid rgba(255,255,255,0.06); -} - -/* ---- Vitals Panel (left) ---- */ -#panel-vitals { - left: 28px; - top: 50%; - transform: translateY(-50%); -} - -.vital-row { - display: flex; - align-items: flex-start; - gap: 12px; - margin-bottom: 18px; -} -.vital-row:last-child { margin-bottom: 0; } - -.vital-icon { - font-size: 20px; - line-height: 1; - margin-top: 2px; - width: 24px; - text-align: center; -} - -.vital-row:nth-child(2) .vital-icon { color: var(--red-heart); } -.vital-row:nth-child(3) .vital-icon { color: var(--green-glow); } -.vital-row:nth-child(4) .vital-icon { color: var(--amber); } - -.vital-data { flex: 1; } - -.vital-label { - font-size: 10px; - color: var(--text-label); - letter-spacing: 1px; - text-transform: uppercase; - margin-bottom: 3px; -} - -.vital-value { - font-family: 'JetBrains Mono', monospace; - font-size: 26px; - font-weight: 600; - line-height: 1.1; -} - -.vital-unit { - font-size: 12px; - font-weight: 400; - color: var(--text-secondary); -} - -.vital-bar { - height: 3px; - background: rgba(255,255,255,0.06); - border-radius: 2px; - margin-top: 6px; - overflow: hidden; -} - -.vital-bar-fill { - height: 100%; - border-radius: 2px; - transition: width 0.5s ease; -} - -.vital-bar--hr { background: var(--red-heart); width: 0%; } -.vital-bar--br { background: var(--green-glow); width: 0%; } -.vital-bar--conf { background: var(--amber); width: 0%; } - -/* ---- Signal Panel (right) ---- */ -#panel-signal { - right: 28px; - top: 50%; - transform: translateY(-50%); -} - -.signal-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; -} - -.signal-label { - font-size: 11px; - color: var(--text-label); - letter-spacing: 0.5px; -} - -.signal-value { - font-family: 'JetBrains Mono', monospace; - font-size: 13px; - font-weight: 600; - color: var(--blue-signal); -} - -#rssi-sparkline { - width: 100%; - height: 48px; - margin-top: 8px; - border-radius: 6px; - background: rgba(0,0,0,0.3); -} - -/* Presence */ -.presence-state { - text-align: center; - padding: 8px; - border-radius: 8px; - font-family: 'JetBrains Mono', monospace; - font-size: 14px; - font-weight: 600; - letter-spacing: 2px; - transition: all 0.5s ease; -} - -.presence--absent { - background: rgba(255,255,255,0.03); - color: var(--text-label); - border: 1px solid rgba(255,255,255,0.05); -} - -.presence--present { - background: rgba(0, 216, 120, 0.1); - color: var(--green-glow); - border: 1px solid rgba(0, 216, 120, 0.3); - box-shadow: 0 0 20px rgba(0, 216, 120, 0.1); -} - -.presence--active { - background: rgba(255, 176, 32, 0.1); - color: var(--amber); - border: 1px solid rgba(255, 176, 32, 0.3); - box-shadow: 0 0 20px rgba(255, 176, 32, 0.1); -} - -.fall-alert { - margin-top: 10px; - text-align: center; - padding: 8px; - border-radius: 8px; - font-family: 'JetBrains Mono', monospace; - font-size: 12px; - font-weight: 700; - letter-spacing: 2px; - background: rgba(255, 48, 64, 0.15); - color: var(--red-alert); - border: 1px solid rgba(255, 48, 64, 0.4); - animation: pulse-alert 0.8s infinite; -} - -@keyframes pulse-alert { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -/* ---- Capabilities Bar (bottom center) ---- */ -#capabilities-bar { - position: absolute; - bottom: 20px; - left: 50%; - transform: translateX(-50%); - display: flex; - align-items: center; - gap: 0; - background: var(--bg-panel); - border: 1px solid var(--bg-panel-border); - border-radius: 30px; - padding: 8px 24px; - backdrop-filter: blur(12px); -} - -.cap-item { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 500; - color: var(--text-secondary); - padding: 0 16px; -} - -.cap-icon { - font-size: 16px; - color: var(--green-glow); -} - -.cap-item:nth-child(3) .cap-icon { color: var(--red-heart); } -.cap-item:nth-child(5) .cap-icon { color: var(--blue-signal); } - -.cap-divider { - width: 1px; - height: 20px; - background: rgba(255,255,255,0.1); -} - -/* ---- Key hints ---- */ -#key-hints { - position: absolute; - bottom: 24px; - right: 28px; - display: flex; - gap: 8px; -} - -.key-hint { - font-family: 'JetBrains Mono', monospace; - font-size: 10px; - color: rgba(255,255,255,0.2); - letter-spacing: 0.5px; - padding: 3px 8px; - border-radius: 4px; - background: rgba(255,255,255,0.03); - border: 1px solid rgba(255,255,255,0.05); -} - -/* ---- Settings button ---- */ -#settings-btn { - pointer-events: auto; - background: rgba(255,255,255,0.06); - border: 1px solid rgba(255,255,255,0.1); - color: var(--text-secondary); - font-size: 18px; - width: 34px; height: 34px; - border-radius: 50%; - cursor: pointer; - transition: all 0.2s; - display: flex; align-items: center; justify-content: center; - padding: 0; -} -#settings-btn:hover { - background: rgba(0, 216, 120, 0.15); - border-color: var(--green-glow); - color: var(--green-glow); -} - -/* ---- Settings Dialog ---- */ -.settings-overlay { - position: fixed; - top: 0; left: 0; - width: 100%; height: 100%; - z-index: 100; - background: rgba(0,0,0,0.5); - backdrop-filter: blur(4px); - display: flex; - align-items: center; - justify-content: center; - pointer-events: auto; -} - -.settings-dialog { - background: rgba(10, 16, 28, 0.96); - border: 1px solid rgba(0, 216, 120, 0.2); - border-radius: 16px; - width: 440px; - max-height: 80vh; - overflow-y: auto; - padding: 0; - box-shadow: 0 20px 60px rgba(0,0,0,0.6), 0 0 40px rgba(0,216,120,0.05); -} - -.settings-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 20px; - border-bottom: 1px solid rgba(255,255,255,0.06); - font-family: 'JetBrains Mono', monospace; - font-size: 13px; - font-weight: 600; - letter-spacing: 1px; - text-transform: uppercase; - color: var(--text-primary); -} - -.settings-header button { - background: none; - border: none; - color: var(--text-secondary); - font-size: 22px; - cursor: pointer; - padding: 0 4px; - line-height: 1; -} -.settings-header button:hover { color: var(--red-alert); } - -.settings-tabs { - display: flex; - border-bottom: 1px solid rgba(255,255,255,0.06); - padding: 0 12px; -} - -.stab { - background: none; - border: none; - color: var(--text-label); - font-family: 'JetBrains Mono', monospace; - font-size: 10px; - letter-spacing: 1px; - text-transform: uppercase; - padding: 10px 14px; - cursor: pointer; - border-bottom: 2px solid transparent; - transition: all 0.2s; -} -.stab:hover { color: var(--text-secondary); } -.stab.active { - color: var(--green-glow); - border-bottom-color: var(--green-glow); -} - -.stab-content { - display: none; - padding: 16px 20px; -} -.stab-content.active { display: block; } - -.setting-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 14px; - font-size: 12px; - color: var(--text-secondary); -} - -.setting-row span:first-child { - min-width: 120px; - flex-shrink: 0; -} - -.setting-row input[type="range"] { - flex: 1; - height: 4px; - -webkit-appearance: none; - appearance: none; - background: rgba(255,255,255,0.08); - border-radius: 2px; - outline: none; -} -.setting-row input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - width: 14px; height: 14px; - border-radius: 50%; - background: var(--green-glow); - cursor: pointer; - box-shadow: 0 0 6px rgba(0,216,120,0.4); -} - -.setting-row input[type="color"] { - -webkit-appearance: none; - width: 36px; height: 24px; - border: 1px solid rgba(255,255,255,0.15); - border-radius: 4px; - background: none; - cursor: pointer; - padding: 0; -} -.setting-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; } -.setting-row input[type="color"]::-webkit-color-swatch { border-radius: 2px; border: none; } - -.setting-row select, -.setting-row input[type="text"] { - flex: 1; - background: #0c1420; - border: 1px solid rgba(255,255,255,0.1); - color: var(--text-primary); - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - padding: 6px 10px; - border-radius: 6px; - outline: none; -} -.setting-row select:focus, -.setting-row input[type="text"]:focus { - border-color: var(--green-glow); -} -.setting-row select option { - background: #0c1420; - color: var(--text-primary); - padding: 6px 10px; -} -.setting-row select optgroup { - background: #0a1018; - color: var(--green-glow); - font-style: normal; - font-weight: 600; - padding: 4px 0; -} - -.setting-row input[type="checkbox"] { - width: 18px; height: 18px; - accent-color: var(--green-glow); - cursor: pointer; -} - -.check-row { - flex-direction: row; -} - -.range-val { - font-family: 'JetBrains Mono', monospace; - font-size: 10px; - color: var(--green-glow); - min-width: 44px; - text-align: right; -} - -.settings-btn { - width: 100%; - padding: 8px; - margin-top: 6px; - background: rgba(0, 216, 120, 0.08); - border: 1px solid rgba(0, 216, 120, 0.2); - color: var(--green-glow); - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - letter-spacing: 1px; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s; -} -.settings-btn:hover { - background: rgba(0, 216, 120, 0.15); - border-color: var(--green-glow); -} - -/* ---- Scenario Description ---- */ -#scenario-description { - position: absolute; - top: 60px; - right: 28px; - max-width: 340px; - font-size: 11px; - color: var(--text-secondary); - font-style: italic; - letter-spacing: 0.3px; - line-height: 1.4; - pointer-events: none; - opacity: 0.7; - transition: opacity 0.5s ease; -} - -/* ---- Edge Module Badges ---- */ -#edge-modules-bar { - position: absolute; - bottom: 58px; - left: 50%; - transform: translateX(-50%); - display: flex; - align-items: center; - gap: 6px; - pointer-events: none; -} - -.edge-badge { - display: inline-block; - padding: 2px 8px; - border-radius: 10px; - font-family: 'JetBrains Mono', monospace; - font-size: 9px; - font-weight: 600; - letter-spacing: 1px; - color: var(--badge-color, var(--text-secondary)); - background: rgba(255,255,255,0.04); - border: 1px solid var(--badge-color, rgba(255,255,255,0.1)); - box-shadow: 0 0 6px color-mix(in srgb, var(--badge-color, transparent) 30%, transparent); -} - -/* ---- Person Count Dots ---- */ -.persons-dots { - display: inline-flex; - align-items: center; - gap: 3px; - margin-left: 6px; - vertical-align: middle; -} - -.person-dot { - width: 6px; - height: 6px; - border-radius: 50%; - display: inline-block; - background: rgba(255,255,255,0.08); - border: 1px solid rgba(255,255,255,0.1); - transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease; -} - -.person-dot--active { - background: var(--green-glow); - border-color: var(--green-glow); - box-shadow: 0 0 4px rgba(0, 216, 120, 0.4); -} - -/* ---- Vital Value Color Transitions ---- */ -.vital-value span:first-child { - transition: color 0.6s ease; -} - -.vital-bar-fill { - transition: width 0.5s ease, background 0.6s ease; -} - -/* ---- Responsive ---- */ -@media (max-width: 1200px) { - .data-panel { width: 190px; padding: 12px; } - .vital-value { font-size: 22px; } - #capabilities-bar { display: none; } -} - -@media (max-width: 800px) { - /* Brand — smaller, top-left */ - #brand { top: 12px; left: 14px; } - #brand-logo { font-size: 22px; } - #brand-tagline { font-size: 9px; letter-spacing: 1px; } - - /* Status bar — compact, below brand */ - #status-bar { - top: 12px; right: 14px; - gap: 6px; - } - #data-source-badge { padding: 3px 8px; font-size: 9px; } - #scenario-area { padding: 3px 10px; } - #scenario-quick-select { font-size: 10px; } - #settings-btn { width: 30px; height: 30px; font-size: 15px; } - - /* Scenario description — under status bar */ - #scenario-description { - top: 46px; right: 14px; - max-width: 200px; - font-size: 10px; - } - - /* Data panels — horizontal strip at bottom instead of side panels */ - .data-panel { - position: fixed; - width: auto; - left: 8px; - right: 8px; - top: auto; - transform: none; - border-radius: 10px; - padding: 10px 14px; - display: flex; - flex-wrap: wrap; - gap: 4px 16px; - align-items: center; - } - - #panel-vitals { - bottom: 100px; - left: 8px; - right: 8px; - transform: none; - } - #panel-vitals .panel-header { display: none; } - #panel-vitals .vital-row { - margin-bottom: 0; - flex: 1; - min-width: 90px; - } - #panel-vitals .vital-icon { font-size: 16px; } - #panel-vitals .vital-value { font-size: 18px; } - #panel-vitals .vital-bar { display: none; } - #panel-vitals .vital-label { font-size: 8px; } - #panel-vitals .vital-unit { font-size: 10px; } - - #panel-signal { - bottom: 8px; - left: 8px; - right: 8px; - transform: none; - } - #panel-signal .panel-header { display: none; } - #panel-signal .signal-row { margin-bottom: 2px; } - #panel-signal .signal-label { font-size: 10px; } - #panel-signal .signal-value { font-size: 11px; } - #panel-signal #rssi-sparkline { display: none; } - #panel-signal .presence-state { - display: inline-block; - padding: 4px 12px; - font-size: 11px; - } - #panel-signal .fall-alert { padding: 4px 8px; font-size: 10px; } - - /* Key hints — hidden on mobile (no keyboard) */ - #key-hints { display: none; } - - /* Capabilities bar — hidden */ - #capabilities-bar { display: none; } - - /* Edge modules — smaller */ - #edge-modules-bar { bottom: 196px; } - .edge-badge { font-size: 8px; padding: 1px 6px; } - - /* Settings dialog — full width on mobile */ - .settings-dialog { - width: 96vw; - max-height: 85vh; - border-radius: 12px; - } - .settings-header { padding: 12px 16px; font-size: 12px; } - .stab { padding: 8px 10px; font-size: 9px; } - .stab-content { padding: 12px 16px; } - .setting-row { font-size: 11px; gap: 8px; margin-bottom: 10px; } - .setting-row span:first-child { min-width: 90px; } -} - -/* Extra small screens (phones in portrait) */ -@media (max-width: 480px) { - #brand-tagline { display: none; } - #scenario-area { display: none; } - #scenario-description { display: none; } - - #panel-vitals { - bottom: 70px; - } - #panel-vitals .vital-row { min-width: 70px; } - #panel-vitals .vital-value { font-size: 16px; } - - #panel-signal { - flex-wrap: nowrap; - overflow-x: auto; - gap: 2px 12px; - } - - #edge-modules-bar { display: none; } -} diff --git a/ui/pose-fusion.html b/ui/pose-fusion.html deleted file mode 100644 index 2b023c6f..00000000 --- a/ui/pose-fusion.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - WiFi-DensePose — Dual-Modal Pose Estimation - - - - - -
-
- -
Dual-Modal Pose Estimation — Live Video + WiFi CSI Fusion
-
-
- -
- - READY -
- -- FPS - ← Dashboard - Observatory → -
-
- - -
- - -
- - -
DUAL FUSION
- -
-

Enable your webcam for live video pose estimation.
- Or switch to CSI Only mode for WiFi-based sensing.

- -
-
- - -
- - -
-
◆ Fusion Confidence
-
-
- Video -
- 0% -
-
- CSI -
- 0% -
-
- Fused -
- 0% -
-
-
- Cross-modal: 0.000 -
-
- - -
-
◆ CSI Amplitude Heatmap
-
- -
-
- - -
-
◆ Embedding Space (2D Projection)
-
- -
-
- - -
-
◆ Pipeline Latency
-
-
-
--
-
Video CNN
-
-
-
--
-
CSI CNN
-
-
-
--
-
Fusion
-
-
-
--
-
Total
-
-
-
- - -
-
◆ Controls
-
- -
- -
- - - 0.30 -
- -
-
◆ Live CSI Source
-
- - -
-
-
- -
- - -
-
- WiFi-DensePose · Dual-Modal Pose Estimation · - Architecture: MobileNet-V3 × 2 → Attention Fusion → 17-Keypoint COCO -
-
- GitHub · - CNN: ruvector-cnn (JS fallback) · - Observatory -
-
- -
- - - - diff --git a/ui/pose-fusion/build.sh b/ui/pose-fusion/build.sh deleted file mode 100644 index 4d76eba2..00000000 --- a/ui/pose-fusion/build.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Build WASM packages for the dual-modal pose estimation demo. -# Requires: wasm-pack (cargo install wasm-pack) -# -# Usage: ./build.sh -# -# Output: pkg/ruvector_cnn_wasm/ — WASM CNN embedder for browser - -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -VENDOR_DIR="$SCRIPT_DIR/../../vendor/ruvector" -OUT_DIR="$SCRIPT_DIR/pkg/ruvector_cnn_wasm" - -echo "Building ruvector-cnn-wasm..." -wasm-pack build "$VENDOR_DIR/crates/ruvector-cnn-wasm" \ - --target web \ - --out-dir "$OUT_DIR" \ - --no-typescript - -# Remove .gitignore so we can commit the build output for GitHub Pages -rm -f "$OUT_DIR/.gitignore" - -echo "" -echo "Build complete!" -echo " WASM: $(du -sh "$OUT_DIR/ruvector_cnn_wasm_bg.wasm" | cut -f1)" -echo " JS: $(du -sh "$OUT_DIR/ruvector_cnn_wasm.js" | cut -f1)" -echo "" -echo "Serve the demo: cd $SCRIPT_DIR/.. && python3 -m http.server 8080" -echo "Open: http://localhost:8080/pose-fusion.html" diff --git a/ui/pose-fusion/css/style.css b/ui/pose-fusion/css/style.css deleted file mode 100644 index 0cbefe19..00000000 --- a/ui/pose-fusion/css/style.css +++ /dev/null @@ -1,403 +0,0 @@ -/* WiFi-DensePose — Dual-Modal Pose Fusion Demo - Dark theme matching Observatory */ - -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;600&display=swap'); - -:root { - --bg-deep: #080c14; - --bg-panel: rgba(8, 16, 28, 0.92); - --bg-panel-border: rgba(0, 210, 120, 0.25); - --green-glow: #00d878; - --green-bright:#3eff8a; - --green-dim: #0a6b3a; - --amber: #ffb020; - --amber-dim: #a06800; - --blue-signal: #2090ff; - --blue-dim: #0a3060; - --red-alert: #ff3040; - --cyan: #00e5ff; - --text-primary: #e8ece0; - --text-secondary: rgba(232,236,224, 0.55); - --text-label: rgba(232,236,224, 0.35); - --radius: 8px; -} - -* { margin: 0; padding: 0; box-sizing: border-box; } - -body { - background: var(--bg-deep); - font-family: 'Inter', -apple-system, sans-serif; - color: var(--text-primary); - -webkit-font-smoothing: antialiased; - overflow-x: hidden; - min-height: 100vh; -} - -/* === Header === */ -.header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 24px; - border-bottom: 1px solid var(--bg-panel-border); - background: var(--bg-panel); - backdrop-filter: blur(12px); -} - -.header-left { - display: flex; - align-items: center; - gap: 16px; -} - -.logo { - font-weight: 700; - font-size: 24px; - color: var(--green-glow); -} - -.logo .pi { font-style: normal; } - -.header-title { - font-size: 14px; - color: var(--text-secondary); - font-weight: 300; -} - -.header-right { - display: flex; - align-items: center; - gap: 16px; -} - -.mode-select { - background: rgba(0,210,120,0.1); - border: 1px solid var(--bg-panel-border); - color: var(--text-primary); - padding: 6px 12px; - border-radius: var(--radius); - font-family: inherit; - font-size: 13px; - cursor: pointer; -} - -.mode-select option { background: #0c1420; } - -.status-badge { - display: flex; - align-items: center; - gap: 6px; - font-family: 'JetBrains Mono', monospace; - font-size: 12px; - padding: 4px 10px; - border-radius: 12px; - background: rgba(0,210,120,0.1); - border: 1px solid var(--bg-panel-border); -} - -.status-dot { - width: 8px; height: 8px; - border-radius: 50%; - background: var(--green-glow); - box-shadow: 0 0 8px var(--green-glow); - animation: pulse-dot 2s ease infinite; -} - -.status-dot.offline { background: #555; box-shadow: none; animation: none; } -.status-dot.warning { background: var(--amber); box-shadow: 0 0 8px var(--amber); } - -@keyframes pulse-dot { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -.fps-badge { - font-family: 'JetBrains Mono', monospace; - font-size: 12px; - color: var(--green-glow); -} - -.back-link { - color: var(--text-secondary); - text-decoration: none; - font-size: 13px; - transition: color 0.2s; -} -.back-link:hover { color: var(--green-glow); } - -/* === Main Layout === */ -.main-grid { - display: grid; - grid-template-columns: 1fr 360px; - grid-template-rows: auto auto; - gap: 16px; - padding: 16px 24px; - max-height: calc(100vh - 72px); -} - -/* === Video Panel === */ -.video-panel { - position: relative; - background: #000; - border-radius: var(--radius); - border: 1px solid var(--bg-panel-border); - overflow: hidden; - aspect-ratio: 4/3; - max-height: 60vh; -} - -.video-panel video { - width: 100%; - height: 100%; - object-fit: cover; - transform: scaleX(-1); -} - -.video-panel canvas { - position: absolute; - top: 0; left: 0; - width: 100%; - height: 100%; - transform: scaleX(-1); -} - -.video-overlay-label { - position: absolute; - top: 12px; left: 12px; - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - padding: 4px 8px; - background: rgba(0,0,0,0.7); - border-radius: 4px; - color: var(--green-glow); - z-index: 5; - transform: scaleX(-1); -} - -.camera-prompt { - position: absolute; - top: 50%; left: 50%; - transform: translate(-50%, -50%); - text-align: center; - color: var(--text-secondary); -} - -.camera-prompt button { - margin-top: 12px; - padding: 10px 24px; - background: var(--green-glow); - color: #000; - border: none; - border-radius: var(--radius); - font-family: inherit; - font-weight: 600; - font-size: 14px; - cursor: pointer; - transition: background 0.2s; -} - -.camera-prompt button:hover { background: var(--green-bright); } - -/* === Side Panels === */ -.side-panels { - display: flex; - flex-direction: column; - gap: 12px; - overflow-y: auto; - max-height: calc(100vh - 88px); -} - -.panel { - background: var(--bg-panel); - border: 1px solid var(--bg-panel-border); - border-radius: var(--radius); - padding: 14px; -} - -.panel-title { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 1.2px; - color: var(--text-label); - margin-bottom: 10px; - display: flex; - align-items: center; - gap: 6px; -} - -/* === CSI Heatmap === */ -.csi-canvas-wrapper { - position: relative; - border-radius: 4px; - overflow: hidden; - background: #000; -} - -.csi-canvas-wrapper canvas { - width: 100%; - display: block; -} - -/* === Fusion Bars === */ -.fusion-bars { - display: flex; - flex-direction: column; - gap: 8px; -} - -.bar-row { - display: flex; - align-items: center; - gap: 8px; -} - -.bar-label { - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - color: var(--text-secondary); - width: 55px; - text-align: right; -} - -.bar-track { - flex: 1; - height: 6px; - background: rgba(255,255,255,0.06); - border-radius: 3px; - overflow: hidden; -} - -.bar-fill { - height: 100%; - border-radius: 3px; - transition: width 0.3s ease; -} - -.bar-fill.video { background: var(--cyan); } -.bar-fill.csi { background: var(--amber); } -.bar-fill.fused { background: var(--green-glow); box-shadow: 0 0 8px var(--green-glow); } - -.bar-value { - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - color: var(--text-primary); - width: 36px; -} - -/* === Embedding Space === */ -.embedding-canvas-wrapper { - position: relative; - background: #000; - border-radius: 4px; - overflow: hidden; -} -.embedding-canvas-wrapper canvas { - width: 100%; - display: block; -} - -/* === Latency Panel === */ -.latency-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 6px; -} - -.latency-item { - text-align: center; - padding: 6px 0; -} - -.latency-value { - font-family: 'JetBrains Mono', monospace; - font-size: 16px; - font-weight: 600; - color: var(--green-glow); -} - -.latency-label { - font-size: 10px; - color: var(--text-label); - margin-top: 2px; -} - -/* === Controls === */ -.controls-row { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.btn { - padding: 6px 14px; - border: 1px solid var(--bg-panel-border); - background: rgba(0,210,120,0.08); - color: var(--text-primary); - border-radius: var(--radius); - font-family: inherit; - font-size: 12px; - cursor: pointer; - transition: all 0.2s; -} -.btn:hover { background: rgba(0,210,120,0.2); } -.btn.active { background: var(--green-glow); color: #000; font-weight: 600; } - -.slider-row { - display: flex; - align-items: center; - gap: 8px; - margin-top: 8px; -} - -.slider-row label { - font-size: 11px; - color: var(--text-secondary); - white-space: nowrap; -} - -.slider-row input[type=range] { - flex: 1; - accent-color: var(--green-glow); -} - -.slider-row .slider-val { - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - width: 32px; - color: var(--green-glow); -} - -/* === Bottom Bar === */ -.bottom-bar { - grid-column: 1 / -1; - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 16px; - background: var(--bg-panel); - border: 1px solid var(--bg-panel-border); - border-radius: var(--radius); - font-family: 'JetBrains Mono', monospace; - font-size: 11px; - color: var(--text-secondary); -} - -.bottom-bar a { - color: var(--green-glow); - text-decoration: none; -} - -/* === Skeleton colors === */ -.skeleton-joint { fill: var(--green-glow); } -.skeleton-limb { stroke: var(--green-bright); } -.skeleton-joint-csi { fill: var(--amber); } -.skeleton-limb-csi { stroke: var(--amber); } - -/* === Responsive === */ -@media (max-width: 900px) { - .main-grid { - grid-template-columns: 1fr; - } - .video-panel { aspect-ratio: 16/9; max-height: 40vh; } - .side-panels { max-height: none; } -} diff --git a/ui/pose-fusion/js/canvas-renderer.js b/ui/pose-fusion/js/canvas-renderer.js deleted file mode 100644 index 8ac169d9..00000000 --- a/ui/pose-fusion/js/canvas-renderer.js +++ /dev/null @@ -1,247 +0,0 @@ -/** - * CanvasRenderer — Renders skeleton overlay on video, CSI heatmap, - * embedding space visualization, and fusion confidence bars. - */ - -import { SKELETON_CONNECTIONS } from './pose-decoder.js'; - -export class CanvasRenderer { - constructor() { - this.colors = { - joint: '#00d878', - jointGlow: 'rgba(0, 216, 120, 0.4)', - limb: '#3eff8a', - limbGlow: 'rgba(62, 255, 138, 0.15)', - csiJoint: '#ffb020', - csiLimb: '#ffc850', - fused: '#00e5ff', - confidence: 'rgba(255,255,255,0.3)', - videoEmb: '#00e5ff', - csiEmb: '#ffb020', - fusedEmb: '#00d878', - }; - } - - /** - * Draw skeleton overlay on the video canvas - * @param {CanvasRenderingContext2D} ctx - * @param {Array<{x,y,confidence}>} keypoints - Normalized [0,1] coordinates - * @param {number} width - Canvas width - * @param {number} height - Canvas height - * @param {object} opts - */ - drawSkeleton(ctx, keypoints, width, height, opts = {}) { - const minConf = opts.minConfidence || 0.3; - const color = opts.color || 'green'; - const jointColor = color === 'amber' ? this.colors.csiJoint : this.colors.joint; - const limbColor = color === 'amber' ? this.colors.csiLimb : this.colors.limb; - const glowColor = color === 'amber' ? 'rgba(255,176,32,0.4)' : this.colors.jointGlow; - - ctx.clearRect(0, 0, width, height); - - if (!keypoints || keypoints.length === 0) return; - - // Draw limbs first (behind joints) - ctx.lineWidth = 3; - ctx.lineCap = 'round'; - - for (const [i, j] of SKELETON_CONNECTIONS) { - const kpA = keypoints[i]; - const kpB = keypoints[j]; - if (!kpA || !kpB || kpA.confidence < minConf || kpB.confidence < minConf) continue; - - const ax = kpA.x * width, ay = kpA.y * height; - const bx = kpB.x * width, by = kpB.y * height; - const avgConf = (kpA.confidence + kpB.confidence) / 2; - - // Glow - ctx.strokeStyle = this.colors.limbGlow; - ctx.lineWidth = 8; - ctx.globalAlpha = avgConf * 0.4; - ctx.beginPath(); - ctx.moveTo(ax, ay); - ctx.lineTo(bx, by); - ctx.stroke(); - - // Main line - ctx.strokeStyle = limbColor; - ctx.lineWidth = 2.5; - ctx.globalAlpha = avgConf; - ctx.beginPath(); - ctx.moveTo(ax, ay); - ctx.lineTo(bx, by); - ctx.stroke(); - } - - // Draw joints - ctx.globalAlpha = 1; - for (const kp of keypoints) { - if (!kp || kp.confidence < minConf) continue; - - const x = kp.x * width; - const y = kp.y * height; - const r = 3 + kp.confidence * 3; - - // Glow - ctx.beginPath(); - ctx.arc(x, y, r + 4, 0, Math.PI * 2); - ctx.fillStyle = glowColor; - ctx.globalAlpha = kp.confidence * 0.6; - ctx.fill(); - - // Joint dot - ctx.beginPath(); - ctx.arc(x, y, r, 0, Math.PI * 2); - ctx.fillStyle = jointColor; - ctx.globalAlpha = kp.confidence; - ctx.fill(); - - // White center - ctx.beginPath(); - ctx.arc(x, y, r * 0.4, 0, Math.PI * 2); - ctx.fillStyle = '#fff'; - ctx.globalAlpha = kp.confidence * 0.8; - ctx.fill(); - } - - ctx.globalAlpha = 1; - - // Confidence label - if (opts.label) { - ctx.font = '11px "JetBrains Mono", monospace'; - ctx.fillStyle = jointColor; - ctx.globalAlpha = 0.8; - ctx.fillText(opts.label, 8, height - 8); - ctx.globalAlpha = 1; - } - } - - /** - * Draw CSI amplitude heatmap - * @param {CanvasRenderingContext2D} ctx - * @param {{ data: Float32Array, width: number, height: number }} heatmap - * @param {number} canvasW - * @param {number} canvasH - */ - drawCsiHeatmap(ctx, heatmap, canvasW, canvasH) { - ctx.clearRect(0, 0, canvasW, canvasH); - - if (!heatmap || !heatmap.data || heatmap.height < 2) { - ctx.fillStyle = '#0a0e18'; - ctx.fillRect(0, 0, canvasW, canvasH); - ctx.font = '11px "JetBrains Mono", monospace'; - ctx.fillStyle = 'rgba(255,255,255,0.3)'; - ctx.fillText('Waiting for CSI data...', 8, canvasH / 2); - return; - } - - const { data, width: dw, height: dh } = heatmap; - const cellW = canvasW / dw; - const cellH = canvasH / dh; - - for (let y = 0; y < dh; y++) { - for (let x = 0; x < dw; x++) { - const val = Math.min(1, Math.max(0, data[y * dw + x])); - ctx.fillStyle = this._heatmapColor(val); - ctx.fillRect(x * cellW, y * cellH, cellW + 0.5, cellH + 0.5); - } - } - - // Axis labels - ctx.font = '9px "JetBrains Mono", monospace'; - ctx.fillStyle = 'rgba(255,255,255,0.4)'; - ctx.fillText('Subcarrier →', 4, canvasH - 4); - ctx.save(); - ctx.translate(canvasW - 4, canvasH - 4); - ctx.rotate(-Math.PI / 2); - ctx.fillText('Time ↑', 0, 0); - ctx.restore(); - } - - /** - * Draw embedding space 2D projection - * @param {CanvasRenderingContext2D} ctx - * @param {{ video: Array, csi: Array, fused: Array }} points - * @param {number} w - * @param {number} h - */ - drawEmbeddingSpace(ctx, points, w, h) { - ctx.fillStyle = '#050810'; - ctx.fillRect(0, 0, w, h); - - // Grid - ctx.strokeStyle = 'rgba(255,255,255,0.05)'; - ctx.lineWidth = 0.5; - for (let i = 0; i <= 4; i++) { - const x = (i / 4) * w; - ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke(); - const y = (i / 4) * h; - ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke(); - } - - // Axes - ctx.strokeStyle = 'rgba(255,255,255,0.1)'; - ctx.lineWidth = 1; - ctx.beginPath(); ctx.moveTo(w / 2, 0); ctx.lineTo(w / 2, h); ctx.stroke(); - ctx.beginPath(); ctx.moveTo(0, h / 2); ctx.lineTo(w, h / 2); ctx.stroke(); - - const drawPoints = (pts, color, size) => { - if (!pts || pts.length === 0) return; - const len = pts.length; - for (let i = 0; i < len; i++) { - const p = pts[i]; - if (!p) continue; - const age = 1 - (i / len) * 0.7; // Fade older points - const px = w / 2 + p[0] * w * 0.35; - const py = h / 2 + p[1] * h * 0.35; - - if (px < 0 || px > w || py < 0 || py > h) continue; - - ctx.beginPath(); - ctx.arc(px, py, size, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.globalAlpha = age * 0.7; - ctx.fill(); - } - }; - - drawPoints(points.video, this.colors.videoEmb, 3); - drawPoints(points.csi, this.colors.csiEmb, 3); - drawPoints(points.fused, this.colors.fusedEmb, 4); - ctx.globalAlpha = 1; - - // Legend - ctx.font = '9px "JetBrains Mono", monospace'; - const legends = [ - { color: this.colors.videoEmb, label: 'Video' }, - { color: this.colors.csiEmb, label: 'CSI' }, - { color: this.colors.fusedEmb, label: 'Fused' }, - ]; - legends.forEach((l, i) => { - const ly = 12 + i * 14; - ctx.fillStyle = l.color; - ctx.beginPath(); - ctx.arc(10, ly - 3, 3, 0, Math.PI * 2); - ctx.fill(); - ctx.fillStyle = 'rgba(255,255,255,0.5)'; - ctx.fillText(l.label, 18, ly); - }); - } - - _heatmapColor(val) { - // Dark blue → cyan → green → yellow → red - if (val < 0.25) { - const t = val / 0.25; - return `rgb(${Math.floor(t * 20)}, ${Math.floor(20 + t * 60)}, ${Math.floor(60 + t * 100)})`; - } else if (val < 0.5) { - const t = (val - 0.25) / 0.25; - return `rgb(${Math.floor(20 + t * 20)}, ${Math.floor(80 + t * 100)}, ${Math.floor(160 - t * 60)})`; - } else if (val < 0.75) { - const t = (val - 0.5) / 0.25; - return `rgb(${Math.floor(40 + t * 180)}, ${Math.floor(180 + t * 75)}, ${Math.floor(100 - t * 80)})`; - } else { - const t = (val - 0.75) / 0.25; - return `rgb(${Math.floor(220 + t * 35)}, ${Math.floor(255 - t * 120)}, ${Math.floor(20 - t * 20)})`; - } - } -} diff --git a/ui/pose-fusion/js/cnn-embedder.js b/ui/pose-fusion/js/cnn-embedder.js deleted file mode 100644 index 5000b9d3..00000000 --- a/ui/pose-fusion/js/cnn-embedder.js +++ /dev/null @@ -1,226 +0,0 @@ -/** - * CNN Embedder — Lightweight MobileNet-V3-style feature extractor. - * - * Architecture mirrors ruvector-cnn: Conv2D → BatchNorm → ReLU → Pool → Project → L2 Normalize - * Uses pre-seeded random weights (deterministic). When ruvector-cnn-wasm is available, - * transparently delegates to the WASM implementation. - * - * Two instances are created: one for video frames, one for CSI pseudo-images. - */ - -// Seeded PRNG for deterministic weight initialization -function mulberry32(seed) { - return function() { - let t = (seed += 0x6D2B79F5); - t = Math.imul(t ^ (t >>> 15), t | 1); - t ^= t + Math.imul(t ^ (t >>> 7), t | 61); - return ((t ^ (t >>> 14)) >>> 0) / 4294967296; - }; -} - -export class CnnEmbedder { - /** - * @param {object} opts - * @param {number} opts.inputSize - Square input dimension (default 56 for speed) - * @param {number} opts.embeddingDim - Output embedding dimension (default 128) - * @param {boolean} opts.normalize - L2 normalize output - * @param {number} opts.seed - PRNG seed for weight init - */ - constructor(opts = {}) { - this.inputSize = opts.inputSize || 56; - this.embeddingDim = opts.embeddingDim || 128; - this.normalize = opts.normalize !== false; - this.wasmEmbedder = null; - - // Initialize weights with deterministic PRNG - const rng = mulberry32(opts.seed || 42); - const randRange = (lo, hi) => lo + rng() * (hi - lo); - - // Conv 3x3: 3 input channels → 16 output channels - this.convWeights = new Float32Array(3 * 3 * 3 * 16); - for (let i = 0; i < this.convWeights.length; i++) { - this.convWeights[i] = randRange(-0.15, 0.15); - } - - // BatchNorm params (16 channels) - this.bnGamma = new Float32Array(16).fill(1.0); - this.bnBeta = new Float32Array(16).fill(0.0); - this.bnMean = new Float32Array(16).fill(0.0); - this.bnVar = new Float32Array(16).fill(1.0); - - // Projection: 16 → embeddingDim - this.projWeights = new Float32Array(16 * this.embeddingDim); - for (let i = 0; i < this.projWeights.length; i++) { - this.projWeights[i] = randRange(-0.1, 0.1); - } - } - - /** - * Try to load WASM embedder from ruvector-cnn-wasm package - * @param {string} wasmPath - Path to the WASM package directory - */ - async tryLoadWasm(wasmPath) { - try { - const mod = await import(`${wasmPath}/ruvector_cnn_wasm.js`); - await mod.default(); - const config = new mod.EmbedderConfig(); - config.input_size = this.inputSize; - config.embedding_dim = this.embeddingDim; - config.normalize = this.normalize; - this.wasmEmbedder = new mod.WasmCnnEmbedder(config); - console.log('[CNN] WASM embedder loaded successfully'); - return true; - } catch (e) { - console.log('[CNN] WASM not available, using JS fallback:', e.message); - return false; - } - } - - /** - * Extract embedding from RGB image data - * @param {Uint8Array} rgbData - RGB pixel data (H*W*3) - * @param {number} width - * @param {number} height - * @returns {Float32Array} embedding vector - */ - extract(rgbData, width, height) { - if (this.wasmEmbedder) { - try { - const result = this.wasmEmbedder.extract(rgbData, width, height); - return new Float32Array(result); - } catch (_) { /* fallback to JS */ } - } - return this._extractJS(rgbData, width, height); - } - - _extractJS(rgbData, width, height) { - // 1. Resize to inputSize × inputSize if needed - const sz = this.inputSize; - let input; - if (width === sz && height === sz) { - input = new Float32Array(rgbData.length); - for (let i = 0; i < rgbData.length; i++) input[i] = rgbData[i] / 255.0; - } else { - input = this._resize(rgbData, width, height, sz, sz); - } - - // 2. ImageNet normalization - const mean = [0.485, 0.456, 0.406]; - const std = [0.229, 0.224, 0.225]; - const pixels = sz * sz; - for (let i = 0; i < pixels; i++) { - input[i * 3] = (input[i * 3] - mean[0]) / std[0]; - input[i * 3 + 1] = (input[i * 3 + 1] - mean[1]) / std[1]; - input[i * 3 + 2] = (input[i * 3 + 2] - mean[2]) / std[2]; - } - - // 3. Conv2D 3x3 (3 → 16 channels) - const convOut = this._conv2d3x3(input, sz, sz, 3, 16); - - // 4. BatchNorm - this._batchNorm(convOut, 16); - - // 5. ReLU - for (let i = 0; i < convOut.length; i++) { - if (convOut[i] < 0) convOut[i] = 0; - } - - // 6. Global average pooling → 16-dim - const outH = sz - 2, outW = sz - 2; - const pooled = new Float32Array(16); - const spatial = outH * outW; - for (let i = 0; i < spatial; i++) { - for (let c = 0; c < 16; c++) { - pooled[c] += convOut[i * 16 + c]; - } - } - for (let c = 0; c < 16; c++) pooled[c] /= spatial; - - // 7. Linear projection → embeddingDim - const emb = new Float32Array(this.embeddingDim); - for (let o = 0; o < this.embeddingDim; o++) { - let sum = 0; - for (let i = 0; i < 16; i++) { - sum += pooled[i] * this.projWeights[i * this.embeddingDim + o]; - } - emb[o] = sum; - } - - // 8. L2 normalize - if (this.normalize) { - let norm = 0; - for (let i = 0; i < emb.length; i++) norm += emb[i] * emb[i]; - norm = Math.sqrt(norm); - if (norm > 1e-8) { - for (let i = 0; i < emb.length; i++) emb[i] /= norm; - } - } - - return emb; - } - - _conv2d3x3(input, H, W, Cin, Cout) { - const outH = H - 2, outW = W - 2; - const output = new Float32Array(outH * outW * Cout); - for (let y = 0; y < outH; y++) { - for (let x = 0; x < outW; x++) { - for (let co = 0; co < Cout; co++) { - let sum = 0; - for (let ky = 0; ky < 3; ky++) { - for (let kx = 0; kx < 3; kx++) { - for (let ci = 0; ci < Cin; ci++) { - const px = ((y + ky) * W + (x + kx)) * Cin + ci; - const wt = (((ky * 3 + kx) * Cin) + ci) * Cout + co; - sum += input[px] * this.convWeights[wt]; - } - } - } - output[(y * outW + x) * Cout + co] = sum; - } - } - } - return output; - } - - _batchNorm(data, channels) { - const spatial = data.length / channels; - for (let i = 0; i < spatial; i++) { - for (let c = 0; c < channels; c++) { - const idx = i * channels + c; - data[idx] = this.bnGamma[c] * (data[idx] - this.bnMean[c]) / Math.sqrt(this.bnVar[c] + 1e-5) + this.bnBeta[c]; - } - } - } - - _resize(rgbData, srcW, srcH, dstW, dstH) { - const output = new Float32Array(dstW * dstH * 3); - const xRatio = srcW / dstW; - const yRatio = srcH / dstH; - for (let y = 0; y < dstH; y++) { - for (let x = 0; x < dstW; x++) { - const sx = Math.min(Math.floor(x * xRatio), srcW - 1); - const sy = Math.min(Math.floor(y * yRatio), srcH - 1); - const srcIdx = (sy * srcW + sx) * 3; - const dstIdx = (y * dstW + x) * 3; - output[dstIdx] = rgbData[srcIdx] / 255.0; - output[dstIdx + 1] = rgbData[srcIdx + 1] / 255.0; - output[dstIdx + 2] = rgbData[srcIdx + 2] / 255.0; - } - } - return output; - } - - /** Cosine similarity between two embeddings */ - static cosineSimilarity(a, b) { - let dot = 0, normA = 0, normB = 0; - for (let i = 0; i < a.length; i++) { - dot += a[i] * b[i]; - normA += a[i] * a[i]; - normB += b[i] * b[i]; - } - normA = Math.sqrt(normA); - normB = Math.sqrt(normB); - if (normA < 1e-8 || normB < 1e-8) return 0; - return dot / (normA * normB); - } -} diff --git a/ui/pose-fusion/js/csi-simulator.js b/ui/pose-fusion/js/csi-simulator.js deleted file mode 100644 index 30999293..00000000 --- a/ui/pose-fusion/js/csi-simulator.js +++ /dev/null @@ -1,242 +0,0 @@ -/** - * CSI Simulator — Generates realistic WiFi Channel State Information data. - * - * In live mode, connects to the sensing server via WebSocket. - * In demo mode, generates synthetic CSI that correlates with detected motion. - * - * Outputs: 3-channel pseudo-image (amplitude, phase, temporal diff) - * matching the ADR-018 frame format expectations. - */ - -export class CsiSimulator { - constructor(opts = {}) { - this.subcarriers = opts.subcarriers || 52; // 802.11n HT20 - this.timeWindow = opts.timeWindow || 56; // frames in sliding window - this.mode = 'demo'; // 'demo' | 'live' - this.ws = null; - - // Circular buffer for CSI frames - this.amplitudeBuffer = []; - this.phaseBuffer = []; - this.frameCount = 0; - - // Noise parameters - this._rng = this._mulberry32(opts.seed || 7); - this._noiseState = new Float32Array(this.subcarriers); - this._baseAmplitude = new Float32Array(this.subcarriers); - this._basePhase = new Float32Array(this.subcarriers); - - // Initialize base CSI profile (empty room) - for (let i = 0; i < this.subcarriers; i++) { - this._baseAmplitude[i] = 0.5 + 0.3 * Math.sin(i * 0.12); - this._basePhase[i] = (i / this.subcarriers) * Math.PI * 2; - } - - // Person influence (updated from video motion) - this.personPresence = 0; - this.personX = 0.5; - this.personY = 0.5; - this.personMotion = 0; - } - - /** - * Connect to live sensing server WebSocket - * @param {string} url - WebSocket URL (e.g. ws://localhost:3030/ws/csi) - */ - async connectLive(url) { - return new Promise((resolve) => { - try { - this.ws = new WebSocket(url); - this.ws.binaryType = 'arraybuffer'; - this.ws.onmessage = (evt) => this._handleLiveFrame(evt.data); - this.ws.onopen = () => { this.mode = 'live'; resolve(true); }; - this.ws.onerror = () => resolve(false); - this.ws.onclose = () => { this.mode = 'demo'; }; - // Timeout after 3s - setTimeout(() => { if (this.mode !== 'live') resolve(false); }, 3000); - } catch { - resolve(false); - } - }); - } - - disconnect() { - if (this.ws) { this.ws.close(); this.ws = null; } - this.mode = 'demo'; - } - - get isLive() { return this.mode === 'live'; } - - /** - * Update person state from video detection (for correlated demo data) - */ - updatePersonState(presence, x, y, motion) { - this.personPresence = presence; - this.personX = x; - this.personY = y; - this.personMotion = motion; - } - - /** - * Generate next CSI frame (demo mode) or return latest live frame - * @param {number} elapsed - Time in seconds - * @returns {{ amplitude: Float32Array, phase: Float32Array, snr: number }} - */ - nextFrame(elapsed) { - const amp = new Float32Array(this.subcarriers); - const phase = new Float32Array(this.subcarriers); - - if (this.mode === 'live' && this._liveAmplitude) { - amp.set(this._liveAmplitude); - phase.set(this._livePhase); - } else { - this._generateDemoFrame(amp, phase, elapsed); - } - - // Push to circular buffer - this.amplitudeBuffer.push(new Float32Array(amp)); - this.phaseBuffer.push(new Float32Array(phase)); - if (this.amplitudeBuffer.length > this.timeWindow) { - this.amplitudeBuffer.shift(); - this.phaseBuffer.shift(); - } - - // SNR estimate - let signalPower = 0, noisePower = 0; - for (let i = 0; i < this.subcarriers; i++) { - signalPower += amp[i] * amp[i]; - noisePower += this._noiseState[i] * this._noiseState[i]; - } - const snr = noisePower > 0 ? 10 * Math.log10(signalPower / noisePower) : 30; - - this.frameCount++; - return { amplitude: amp, phase, snr: Math.max(0, Math.min(40, snr)) }; - } - - /** - * Build 3-channel pseudo-image for CNN input - * @param {number} targetSize - Output image dimension (square) - * @returns {Uint8Array} RGB data (targetSize * targetSize * 3) - */ - buildPseudoImage(targetSize = 56) { - const buf = this.amplitudeBuffer; - const pBuf = this.phaseBuffer; - const frames = buf.length; - if (frames < 2) { - return new Uint8Array(targetSize * targetSize * 3); - } - - const rgb = new Uint8Array(targetSize * targetSize * 3); - - for (let y = 0; y < targetSize; y++) { - const fi = Math.min(Math.floor(y / targetSize * frames), frames - 1); - for (let x = 0; x < targetSize; x++) { - const si = Math.min(Math.floor(x / targetSize * this.subcarriers), this.subcarriers - 1); - const idx = (y * targetSize + x) * 3; - - // R: Amplitude (normalized to 0-255) - const ampVal = buf[fi][si]; - rgb[idx] = Math.min(255, Math.max(0, Math.floor(ampVal * 255))); - - // G: Phase (wrapped to 0-255) - const phaseVal = (pBuf[fi][si] % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI); - rgb[idx + 1] = Math.floor(phaseVal / (2 * Math.PI) * 255); - - // B: Temporal difference - if (fi > 0) { - const diff = Math.abs(buf[fi][si] - buf[fi - 1][si]); - rgb[idx + 2] = Math.min(255, Math.floor(diff * 500)); - } - } - } - - return rgb; - } - - /** - * Get heatmap data for visualization - * @returns {{ data: Float32Array, width: number, height: number }} - */ - getHeatmapData() { - const frames = this.amplitudeBuffer.length; - const w = this.subcarriers; - const h = Math.min(frames, this.timeWindow); - const data = new Float32Array(w * h); - for (let y = 0; y < h; y++) { - const fi = frames - h + y; - if (fi >= 0 && fi < frames) { - for (let x = 0; x < w; x++) { - data[y * w + x] = this.amplitudeBuffer[fi][x]; - } - } - } - return { data, width: w, height: h }; - } - - // === Private === - - _generateDemoFrame(amp, phase, elapsed) { - const rng = this._rng; - const presence = this.personPresence; - const motion = this.personMotion; - const px = this.personX; - - for (let i = 0; i < this.subcarriers; i++) { - // Base CSI profile (frequency-selective channel) - let a = this._baseAmplitude[i]; - let p = this._basePhase[i] + elapsed * 0.05; - - // Environmental noise (correlated across subcarriers) - this._noiseState[i] = 0.95 * this._noiseState[i] + 0.05 * (rng() * 2 - 1) * 0.03; - a += this._noiseState[i]; - - // Person-induced CSI perturbation - if (presence > 0.1) { - // Subcarrier-dependent body reflection (Fresnel zone model) - const freqOffset = (i - this.subcarriers * px) / (this.subcarriers * 0.3); - const bodyReflection = presence * 0.25 * Math.exp(-freqOffset * freqOffset); - - // Motion causes amplitude fluctuation - const motionEffect = motion * 0.15 * Math.sin(elapsed * 3.5 + i * 0.3); - - // Breathing modulation (0.2-0.3 Hz) - const breathing = presence * 0.02 * Math.sin(elapsed * 1.5 + i * 0.05); - - a += bodyReflection + motionEffect + breathing; - p += presence * 0.4 * Math.sin(elapsed * 2.1 + i * 0.15); - } - - amp[i] = Math.max(0, Math.min(1, a)); - phase[i] = p; - } - } - - _handleLiveFrame(data) { - const view = new DataView(data); - // Check ADR-018 magic: 0xC5110001 - if (data.byteLength < 20) return; - const magic = view.getUint32(0, true); - if (magic !== 0xC5110001) return; - - const numSub = Math.min(view.getUint16(8, true), this.subcarriers); - this._liveAmplitude = new Float32Array(this.subcarriers); - this._livePhase = new Float32Array(this.subcarriers); - - const headerSize = 20; - for (let i = 0; i < numSub && (headerSize + i * 4 + 3) < data.byteLength; i++) { - const real = view.getInt16(headerSize + i * 4, true); - const imag = view.getInt16(headerSize + i * 4 + 2, true); - this._liveAmplitude[i] = Math.sqrt(real * real + imag * imag) / 2048; - this._livePhase[i] = Math.atan2(imag, real); - } - } - - _mulberry32(seed) { - return function() { - let t = (seed += 0x6D2B79F5); - t = Math.imul(t ^ (t >>> 15), t | 1); - t ^= t + Math.imul(t ^ (t >>> 7), t | 61); - return ((t ^ (t >>> 14)) >>> 0) / 4294967296; - }; - } -} diff --git a/ui/pose-fusion/js/fusion-engine.js b/ui/pose-fusion/js/fusion-engine.js deleted file mode 100644 index 8ded2e8a..00000000 --- a/ui/pose-fusion/js/fusion-engine.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * FusionEngine — Attention-weighted dual-modal embedding fusion. - * - * Combines visual (camera) and CSI (WiFi) embeddings with dynamic - * confidence gating based on signal quality. - */ - -export class FusionEngine { - /** - * @param {number} embeddingDim - */ - constructor(embeddingDim = 128) { - this.embeddingDim = embeddingDim; - - // Learnable attention weights (initialized to balanced 0.5) - // In production, these would be loaded from trained JSON - this.attentionWeights = new Float32Array(embeddingDim).fill(0.5); - - // Dynamic modality confidence [0, 1] - this.videoConfidence = 1.0; - this.csiConfidence = 0.0; - this.fusedConfidence = 0.5; - - // Smoothing for confidence transitions - this._smoothAlpha = 0.85; - - // Embedding history for visualization - this.recentVideoEmbeddings = []; - this.recentCsiEmbeddings = []; - this.recentFusedEmbeddings = []; - this.maxHistory = 50; - } - - /** - * Update quality-based confidence scores - * @param {number} videoBrightness - [0,1] video brightness quality - * @param {number} videoMotion - [0,1] motion detected - * @param {number} csiSnr - CSI signal-to-noise ratio in dB - * @param {boolean} csiActive - Whether CSI source is connected - */ - updateConfidence(videoBrightness, videoMotion, csiSnr, csiActive) { - // Video confidence: drops with low brightness, boosted by motion - let vc = 0; - if (videoBrightness > 0.05) { - vc = Math.min(1, videoBrightness * 1.5) * 0.7 + Math.min(1, videoMotion * 3) * 0.3; - } - - // CSI confidence: based on SNR and connection status - let cc = 0; - if (csiActive) { - cc = Math.min(1, csiSnr / 25); // 25dB = full confidence - } - - // Smooth transitions - this.videoConfidence = this._smoothAlpha * this.videoConfidence + (1 - this._smoothAlpha) * vc; - this.csiConfidence = this._smoothAlpha * this.csiConfidence + (1 - this._smoothAlpha) * cc; - - // Fused confidence is the max of either (fusion can only help) - this.fusedConfidence = Math.min(1, Math.sqrt( - this.videoConfidence * this.videoConfidence + this.csiConfidence * this.csiConfidence - )); - } - - /** - * Fuse video and CSI embeddings - * @param {Float32Array|null} videoEmb - Visual embedding (or null if video-off) - * @param {Float32Array|null} csiEmb - CSI embedding (or null if CSI-off) - * @param {string} mode - 'dual' | 'video' | 'csi' - * @returns {Float32Array} Fused embedding - */ - fuse(videoEmb, csiEmb, mode = 'dual') { - const dim = this.embeddingDim; - const fused = new Float32Array(dim); - - if (mode === 'video' || !csiEmb) { - if (videoEmb) fused.set(videoEmb); - this._recordEmbedding(videoEmb, null, fused); - return fused; - } - - if (mode === 'csi' || !videoEmb) { - if (csiEmb) fused.set(csiEmb); - this._recordEmbedding(null, csiEmb, fused); - return fused; - } - - // Dual mode: attention-weighted fusion with confidence gating - const totalConf = this.videoConfidence + this.csiConfidence; - const videoWeight = totalConf > 0 ? this.videoConfidence / totalConf : 0.5; - - for (let i = 0; i < dim; i++) { - const alpha = this.attentionWeights[i] * videoWeight + - (1 - this.attentionWeights[i]) * (1 - videoWeight); - fused[i] = alpha * videoEmb[i] + (1 - alpha) * csiEmb[i]; - } - - // Re-normalize - let norm = 0; - for (let i = 0; i < dim; i++) norm += fused[i] * fused[i]; - norm = Math.sqrt(norm); - if (norm > 1e-8) { - for (let i = 0; i < dim; i++) fused[i] /= norm; - } - - this._recordEmbedding(videoEmb, csiEmb, fused); - return fused; - } - - /** - * Get embedding pairs for 2D visualization (PCA projection) - * @returns {{ video: Array, csi: Array, fused: Array }} - */ - getEmbeddingPoints() { - // Simple 2D projection using first two principal components (approximated) - const project = (emb) => { - if (!emb || emb.length < 4) return null; - // Use pairs of dimensions as crude 2D projection - let x = 0, y = 0; - for (let i = 0; i < emb.length; i += 2) { - x += emb[i] * (i % 4 < 2 ? 1 : -1); - if (i + 1 < emb.length) { - y += emb[i + 1] * (i % 4 < 2 ? 1 : -1); - } - } - return [x * 2, y * 2]; // Scale for visibility - }; - - return { - video: this.recentVideoEmbeddings.map(project).filter(Boolean), - csi: this.recentCsiEmbeddings.map(project).filter(Boolean), - fused: this.recentFusedEmbeddings.map(project).filter(Boolean) - }; - } - - /** - * Cross-modal similarity score - * @returns {number} Cosine similarity between latest video and CSI embeddings - */ - getCrossModalSimilarity() { - const v = this.recentVideoEmbeddings[this.recentVideoEmbeddings.length - 1]; - const c = this.recentCsiEmbeddings[this.recentCsiEmbeddings.length - 1]; - if (!v || !c) return 0; - - let dot = 0, na = 0, nb = 0; - for (let i = 0; i < v.length; i++) { - dot += v[i] * c[i]; - na += v[i] * v[i]; - nb += c[i] * c[i]; - } - na = Math.sqrt(na); nb = Math.sqrt(nb); - return (na > 1e-8 && nb > 1e-8) ? dot / (na * nb) : 0; - } - - _recordEmbedding(video, csi, fused) { - if (video) { - this.recentVideoEmbeddings.push(new Float32Array(video)); - if (this.recentVideoEmbeddings.length > this.maxHistory) this.recentVideoEmbeddings.shift(); - } - if (csi) { - this.recentCsiEmbeddings.push(new Float32Array(csi)); - if (this.recentCsiEmbeddings.length > this.maxHistory) this.recentCsiEmbeddings.shift(); - } - this.recentFusedEmbeddings.push(new Float32Array(fused)); - if (this.recentFusedEmbeddings.length > this.maxHistory) this.recentFusedEmbeddings.shift(); - } -} diff --git a/ui/pose-fusion/js/main.js b/ui/pose-fusion/js/main.js deleted file mode 100644 index 0883998e..00000000 --- a/ui/pose-fusion/js/main.js +++ /dev/null @@ -1,295 +0,0 @@ -/** - * WiFi-DensePose — Dual-Modal Pose Estimation Demo - * - * Main orchestration: video capture → CNN embedding → CSI processing → fusion → rendering - */ - -import { VideoCapture } from './video-capture.js'; -import { CsiSimulator } from './csi-simulator.js'; -import { CnnEmbedder } from './cnn-embedder.js'; -import { FusionEngine } from './fusion-engine.js'; -import { PoseDecoder } from './pose-decoder.js'; -import { CanvasRenderer } from './canvas-renderer.js'; - -// === State === -let mode = 'dual'; // 'dual' | 'video' | 'csi' -let isRunning = false; -let isPaused = false; -let startTime = 0; -let frameCount = 0; -let fps = 0; -let lastFpsTime = 0; -let confidenceThreshold = 0.3; - -// Latency tracking -const latency = { video: 0, csi: 0, fusion: 0, total: 0 }; - -// === Components === -const videoCapture = new VideoCapture(document.getElementById('webcam')); -const csiSimulator = new CsiSimulator({ subcarriers: 52, timeWindow: 56 }); -const visualCnn = new CnnEmbedder({ inputSize: 56, embeddingDim: 128, seed: 42 }); -const csiCnn = new CnnEmbedder({ inputSize: 56, embeddingDim: 128, seed: 137 }); -const fusionEngine = new FusionEngine(128); -const poseDecoder = new PoseDecoder(128); -const renderer = new CanvasRenderer(); - -// === Canvas Elements === -const skeletonCanvas = document.getElementById('skeleton-canvas'); -const skeletonCtx = skeletonCanvas.getContext('2d'); -const csiCanvas = document.getElementById('csi-canvas'); -const csiCtx = csiCanvas.getContext('2d'); -const embeddingCanvas = document.getElementById('embedding-canvas'); -const embeddingCtx = embeddingCanvas.getContext('2d'); - -// === UI Elements === -const modeSelect = document.getElementById('mode-select'); -const statusDot = document.getElementById('status-dot'); -const statusLabel = document.getElementById('status-label'); -const fpsDisplay = document.getElementById('fps-display'); -const cameraPrompt = document.getElementById('camera-prompt'); -const startCameraBtn = document.getElementById('start-camera-btn'); -const pauseBtn = document.getElementById('pause-btn'); -const confSlider = document.getElementById('confidence-slider'); -const confValue = document.getElementById('confidence-value'); -const wsUrlInput = document.getElementById('ws-url'); -const connectWsBtn = document.getElementById('connect-ws-btn'); - -// Fusion bar elements -const videoBar = document.getElementById('video-bar'); -const csiBar = document.getElementById('csi-bar'); -const fusedBar = document.getElementById('fused-bar'); -const videoBarVal = document.getElementById('video-bar-val'); -const csiBarVal = document.getElementById('csi-bar-val'); -const fusedBarVal = document.getElementById('fused-bar-val'); - -// Latency elements -const latVideoEl = document.getElementById('lat-video'); -const latCsiEl = document.getElementById('lat-csi'); -const latFusionEl = document.getElementById('lat-fusion'); -const latTotalEl = document.getElementById('lat-total'); - -// Cross-modal similarity -const crossModalEl = document.getElementById('cross-modal-sim'); - -// === Initialize === -function init() { - resizeCanvases(); - window.addEventListener('resize', resizeCanvases); - - // Mode change - modeSelect.addEventListener('change', (e) => { - mode = e.target.value; - updateModeUI(); - }); - - // Camera start - startCameraBtn.addEventListener('click', startCamera); - - // Pause - pauseBtn.addEventListener('click', () => { - isPaused = !isPaused; - pauseBtn.textContent = isPaused ? '▶ Resume' : '⏸ Pause'; - pauseBtn.classList.toggle('active', isPaused); - }); - - // Confidence slider - confSlider.addEventListener('input', (e) => { - confidenceThreshold = parseFloat(e.target.value); - confValue.textContent = confidenceThreshold.toFixed(2); - }); - - // WebSocket connect - connectWsBtn.addEventListener('click', async () => { - const url = wsUrlInput.value.trim(); - if (!url) return; - connectWsBtn.textContent = 'Connecting...'; - const ok = await csiSimulator.connectLive(url); - connectWsBtn.textContent = ok ? '✓ Connected' : 'Connect'; - if (ok) { - connectWsBtn.classList.add('active'); - } - }); - - // Try to load WASM embedders (non-blocking) - visualCnn.tryLoadWasm('./pkg/ruvector_cnn_wasm'); - csiCnn.tryLoadWasm('./pkg/ruvector_cnn_wasm'); - - // Auto-start camera for video/dual modes - updateModeUI(); - startTime = performance.now() / 1000; - isRunning = true; - requestAnimationFrame(mainLoop); -} - -async function startCamera() { - cameraPrompt.style.display = 'none'; - const ok = await videoCapture.start(); - if (ok) { - statusDot.classList.remove('offline'); - statusLabel.textContent = 'LIVE'; - resizeCanvases(); - } else { - cameraPrompt.style.display = 'flex'; - cameraPrompt.querySelector('p').textContent = 'Camera access denied. Try CSI-only mode.'; - } -} - -function updateModeUI() { - const needsVideo = mode !== 'csi'; - const needsCsi = mode !== 'video'; - - // Show/hide camera prompt - if (needsVideo && !videoCapture.isActive) { - cameraPrompt.style.display = 'flex'; - } else { - cameraPrompt.style.display = 'none'; - } -} - -function resizeCanvases() { - const videoPanel = document.querySelector('.video-panel'); - if (videoPanel) { - const rect = videoPanel.getBoundingClientRect(); - skeletonCanvas.width = rect.width; - skeletonCanvas.height = rect.height; - } - - // CSI canvas - csiCanvas.width = csiCanvas.parentElement.clientWidth; - csiCanvas.height = 120; - - // Embedding canvas - embeddingCanvas.width = embeddingCanvas.parentElement.clientWidth; - embeddingCanvas.height = 140; -} - -// === Main Loop === -function mainLoop(timestamp) { - if (!isRunning) return; - requestAnimationFrame(mainLoop); - - if (isPaused) return; - - const elapsed = performance.now() / 1000 - startTime; - const totalStart = performance.now(); - - // --- Video Pipeline --- - let videoEmb = null; - let motionRegion = null; - if (mode !== 'csi' && videoCapture.isActive) { - const t0 = performance.now(); - const frame = videoCapture.captureFrame(56, 56); - if (frame) { - videoEmb = visualCnn.extract(frame.rgb, frame.width, frame.height); - motionRegion = videoCapture.detectMotionRegion(56, 56); - - // Feed motion to CSI simulator for correlated demo data - if (motionRegion.detected) { - csiSimulator.updatePersonState( - 1.0, - motionRegion.x + motionRegion.w / 2, - motionRegion.y + motionRegion.h / 2, - frame.motion - ); - } else { - csiSimulator.updatePersonState(0, 0.5, 0.5, 0); - } - - fusionEngine.updateConfidence( - frame.brightness, frame.motion, - 0, csiSimulator.isLive || mode === 'dual' - ); - } - latency.video = performance.now() - t0; - } - - // --- CSI Pipeline --- - let csiEmb = null; - if (mode !== 'video') { - const t0 = performance.now(); - const csiFrame = csiSimulator.nextFrame(elapsed); - const pseudoImage = csiSimulator.buildPseudoImage(56); - csiEmb = csiCnn.extract(pseudoImage, 56, 56); - - fusionEngine.updateConfidence( - videoCapture.brightnessScore, - videoCapture.motionScore, - csiFrame.snr, - true - ); - - // Draw CSI heatmap - const heatmap = csiSimulator.getHeatmapData(); - renderer.drawCsiHeatmap(csiCtx, heatmap, csiCanvas.width, csiCanvas.height); - - latency.csi = performance.now() - t0; - } - - // --- Fusion --- - const t0f = performance.now(); - const fusedEmb = fusionEngine.fuse(videoEmb, csiEmb, mode); - latency.fusion = performance.now() - t0f; - - // --- Pose Decode --- - // For CSI-only mode, generate a synthetic motion region from CSI energy - if (mode === 'csi' && !motionRegion) { - const csiPresence = csiSimulator.personPresence; - if (csiPresence > 0.1) { - motionRegion = { - detected: true, - x: 0.25, y: 0.15, w: 0.5, h: 0.7, - coverage: csiPresence - }; - } - } - - const keypoints = poseDecoder.decode(fusedEmb, motionRegion, elapsed); - - // --- Render Skeleton --- - const labelMap = { dual: 'DUAL FUSION', video: 'VIDEO ONLY', csi: 'CSI ONLY' }; - renderer.drawSkeleton(skeletonCtx, keypoints, skeletonCanvas.width, skeletonCanvas.height, { - minConfidence: confidenceThreshold, - color: mode === 'csi' ? 'amber' : 'green', - label: labelMap[mode] - }); - - // --- Render Embedding Space --- - const embPoints = fusionEngine.getEmbeddingPoints(); - renderer.drawEmbeddingSpace(embeddingCtx, embPoints, embeddingCanvas.width, embeddingCanvas.height); - - // --- Update UI --- - latency.total = performance.now() - totalStart; - - // FPS - frameCount++; - if (timestamp - lastFpsTime > 500) { - fps = Math.round(frameCount * 1000 / (timestamp - lastFpsTime)); - lastFpsTime = timestamp; - frameCount = 0; - fpsDisplay.textContent = `${fps} FPS`; - } - - // Fusion bars - const vc = fusionEngine.videoConfidence; - const cc = fusionEngine.csiConfidence; - const fc = fusionEngine.fusedConfidence; - videoBar.style.width = `${vc * 100}%`; - csiBar.style.width = `${cc * 100}%`; - fusedBar.style.width = `${fc * 100}%`; - videoBarVal.textContent = `${Math.round(vc * 100)}%`; - csiBarVal.textContent = `${Math.round(cc * 100)}%`; - fusedBarVal.textContent = `${Math.round(fc * 100)}%`; - - // Latency - latVideoEl.textContent = `${latency.video.toFixed(1)}ms`; - latCsiEl.textContent = `${latency.csi.toFixed(1)}ms`; - latFusionEl.textContent = `${latency.fusion.toFixed(1)}ms`; - latTotalEl.textContent = `${latency.total.toFixed(1)}ms`; - - // Cross-modal similarity - const sim = fusionEngine.getCrossModalSimilarity(); - crossModalEl.textContent = sim.toFixed(3); -} - -// Boot -document.addEventListener('DOMContentLoaded', init); diff --git a/ui/pose-fusion/js/pose-decoder.js b/ui/pose-fusion/js/pose-decoder.js deleted file mode 100644 index b6befbf7..00000000 --- a/ui/pose-fusion/js/pose-decoder.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * PoseDecoder — Maps fused 512-dim embedding → 17 COCO keypoints. - * - * Uses a learned linear projection (weights shipped as JSON or generated). - * Each keypoint: (x, y, confidence) = 51 values from the embedding. - * - * In demo mode, generates plausible poses from motion detection + embedding features. - */ - -// COCO keypoint definitions -export const KEYPOINT_NAMES = [ - 'nose', 'left_eye', 'right_eye', 'left_ear', 'right_ear', - 'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow', - 'left_wrist', 'right_wrist', 'left_hip', 'right_hip', - 'left_knee', 'right_knee', 'left_ankle', 'right_ankle' -]; - -// Skeleton connections (pairs of keypoint indices) -export const SKELETON_CONNECTIONS = [ - [0, 1], [0, 2], [1, 3], [2, 4], // Head - [5, 6], // Shoulders - [5, 7], [7, 9], // Left arm - [6, 8], [8, 10], // Right arm - [5, 11], [6, 12], // Torso - [11, 12], // Hips - [11, 13], [13, 15], // Left leg - [12, 14], [14, 16], // Right leg -]; - -// Standard body proportions (relative to body height) -const PROPORTIONS = { - headToShoulder: 0.15, - shoulderWidth: 0.25, - shoulderToElbow: 0.18, - elbowToWrist: 0.16, - shoulderToHip: 0.30, - hipWidth: 0.18, - hipToKnee: 0.24, - kneeToAnkle: 0.24, - eyeSpacing: 0.04, - earSpacing: 0.07, -}; - -export class PoseDecoder { - constructor(embeddingDim = 128) { - this.embeddingDim = embeddingDim; - this.smoothedKeypoints = null; - this.smoothingFactor = 0.6; // Temporal smoothing - this._time = 0; - } - - /** - * Decode embedding into 17 keypoints - * @param {Float32Array} embedding - Fused embedding vector - * @param {{ detected: boolean, x: number, y: number, w: number, h: number }} motionRegion - * @param {number} elapsed - Time in seconds - * @returns {Array<{x: number, y: number, confidence: number, name: string}>} - */ - decode(embedding, motionRegion, elapsed) { - this._time = elapsed; - - if (!motionRegion || !motionRegion.detected) { - // Fade out existing pose - if (this.smoothedKeypoints) { - return this.smoothedKeypoints.map(kp => ({ - ...kp, - confidence: kp.confidence * 0.92 - })).filter(kp => kp.confidence > 0.05); - } - return []; - } - - // Generate base pose from motion region - const rawKeypoints = this._generatePoseFromRegion(motionRegion, embedding, elapsed); - - // Apply temporal smoothing - if (this.smoothedKeypoints && this.smoothedKeypoints.length === rawKeypoints.length) { - const alpha = this.smoothingFactor; - for (let i = 0; i < rawKeypoints.length; i++) { - rawKeypoints[i].x = alpha * this.smoothedKeypoints[i].x + (1 - alpha) * rawKeypoints[i].x; - rawKeypoints[i].y = alpha * this.smoothedKeypoints[i].y + (1 - alpha) * rawKeypoints[i].y; - } - } - - this.smoothedKeypoints = rawKeypoints; - return rawKeypoints; - } - - _generatePoseFromRegion(region, embedding, elapsed) { - // Person center and size from motion bounding box - const cx = region.x + region.w / 2; - const cy = region.y + region.h / 2; - const bodyH = Math.max(region.h, 0.3); // Minimum body height - const bodyW = Math.max(region.w, 0.15); - - // Use embedding features to modulate pose - const embMod = this._extractPoseModulation(embedding); - - // Generate COCO keypoints using body proportions - const P = PROPORTIONS; - const halfW = P.shoulderWidth * bodyH / 2; - const hipHalfW = P.hipWidth * bodyH / 2; - - // Breathing animation - const breathe = Math.sin(elapsed * 1.5) * 0.003; - // Subtle sway - const sway = Math.sin(elapsed * 0.7) * 0.005 * embMod.sway; - - // Build from hips up - const hipY = cy + bodyH * 0.15; - const shoulderY = hipY - P.shoulderToHip * bodyH + breathe; - const headY = shoulderY - P.headToShoulder * bodyH; - const kneeY = hipY + P.hipToKnee * bodyH; - const ankleY = kneeY + P.kneeToAnkle * bodyH; - - // Arm animation from motion/embedding - const armSwing = embMod.motion * Math.sin(elapsed * 3) * 0.04; - const armBend = 0.5 + embMod.armBend * 0.3; - - const elbowYL = shoulderY + P.shoulderToElbow * bodyH * armBend; - const elbowYR = shoulderY + P.shoulderToElbow * bodyH * armBend; - const wristYL = elbowYL + P.elbowToWrist * bodyH * armBend; - const wristYR = elbowYR + P.elbowToWrist * bodyH * armBend; - - // Leg animation - const legSwing = embMod.motion * Math.sin(elapsed * 3 + Math.PI) * 0.02; - - const keypoints = [ - // 0: nose - { x: cx + sway, y: headY + 0.01, confidence: 0.9 + embMod.headConf * 0.1 }, - // 1: left_eye - { x: cx - P.eyeSpacing * bodyH + sway, y: headY - 0.005, confidence: 0.85 }, - // 2: right_eye - { x: cx + P.eyeSpacing * bodyH + sway, y: headY - 0.005, confidence: 0.85 }, - // 3: left_ear - { x: cx - P.earSpacing * bodyH, y: headY + 0.005, confidence: 0.7 }, - // 4: right_ear - { x: cx + P.earSpacing * bodyH, y: headY + 0.005, confidence: 0.7 }, - // 5: left_shoulder - { x: cx - halfW + sway * 0.5, y: shoulderY, confidence: 0.92 }, - // 6: right_shoulder - { x: cx + halfW + sway * 0.5, y: shoulderY, confidence: 0.92 }, - // 7: left_elbow - { x: cx - halfW - 0.02 + armSwing, y: elbowYL, confidence: 0.85 }, - // 8: right_elbow - { x: cx + halfW + 0.02 - armSwing, y: elbowYR, confidence: 0.85 }, - // 9: left_wrist - { x: cx - halfW - 0.03 + armSwing * 1.5, y: wristYL, confidence: 0.8 }, - // 10: right_wrist - { x: cx + halfW + 0.03 - armSwing * 1.5, y: wristYR, confidence: 0.8 }, - // 11: left_hip - { x: cx - hipHalfW, y: hipY, confidence: 0.9 }, - // 12: right_hip - { x: cx + hipHalfW, y: hipY, confidence: 0.9 }, - // 13: left_knee - { x: cx - hipHalfW + legSwing, y: kneeY, confidence: 0.87 }, - // 14: right_knee - { x: cx + hipHalfW - legSwing, y: kneeY, confidence: 0.87 }, - // 15: left_ankle - { x: cx - hipHalfW + legSwing * 1.2, y: ankleY, confidence: 0.82 }, - // 16: right_ankle - { x: cx + hipHalfW - legSwing * 1.2, y: ankleY, confidence: 0.82 }, - ]; - - // Add names - for (let i = 0; i < keypoints.length; i++) { - keypoints[i].name = KEYPOINT_NAMES[i]; - } - - return keypoints; - } - - _extractPoseModulation(embedding) { - if (!embedding || embedding.length < 8) { - return { sway: 1, motion: 0.5, armBend: 0.5, headConf: 0.5 }; - } - // Use specific embedding dimensions to modulate pose parameters - return { - sway: 0.5 + embedding[0] * 2, - motion: Math.abs(embedding[1]) * 3, - armBend: 0.5 + embedding[2], - headConf: 0.5 + embedding[3] * 0.5, - }; - } -} diff --git a/ui/pose-fusion/js/video-capture.js b/ui/pose-fusion/js/video-capture.js deleted file mode 100644 index 649311c2..00000000 --- a/ui/pose-fusion/js/video-capture.js +++ /dev/null @@ -1,172 +0,0 @@ -/** - * VideoCapture — getUserMedia webcam capture with frame extraction. - * Provides quality metrics (brightness, motion) for fusion confidence gating. - */ - -export class VideoCapture { - constructor(videoElement) { - this.video = videoElement; - this.stream = null; - this.offscreen = document.createElement('canvas'); - this.offCtx = this.offscreen.getContext('2d', { willReadFrequently: true }); - this.prevFrame = null; - this.motionScore = 0; - this.brightnessScore = 0; - } - - async start(constraints = {}) { - const defaultConstraints = { - video: { - width: { ideal: 640 }, - height: { ideal: 480 }, - facingMode: 'user', - frameRate: { ideal: 30 } - }, - audio: false - }; - - try { - this.stream = await navigator.mediaDevices.getUserMedia( - Object.keys(constraints).length ? constraints : defaultConstraints - ); - this.video.srcObject = this.stream; - await this.video.play(); - - this.offscreen.width = this.video.videoWidth; - this.offscreen.height = this.video.videoHeight; - - return true; - } catch (err) { - console.error('[Video] Camera access failed:', err.message); - return false; - } - } - - stop() { - if (this.stream) { - this.stream.getTracks().forEach(t => t.stop()); - this.stream = null; - } - this.video.srcObject = null; - } - - get isActive() { - return this.stream !== null && this.video.readyState >= 2; - } - - get width() { return this.video.videoWidth || 640; } - get height() { return this.video.videoHeight || 480; } - - /** - * Capture current frame as RGB Uint8Array + compute quality metrics. - * @param {number} targetW - Target width for CNN input - * @param {number} targetH - Target height for CNN input - * @returns {{ rgb: Uint8Array, width: number, height: number, motion: number, brightness: number }} - */ - captureFrame(targetW = 56, targetH = 56) { - if (!this.isActive) return null; - - // Draw to offscreen at target resolution - this.offscreen.width = targetW; - this.offscreen.height = targetH; - this.offCtx.drawImage(this.video, 0, 0, targetW, targetH); - const imageData = this.offCtx.getImageData(0, 0, targetW, targetH); - const rgba = imageData.data; - - // Convert RGBA → RGB - const pixels = targetW * targetH; - const rgb = new Uint8Array(pixels * 3); - let brightnessSum = 0; - let motionSum = 0; - - for (let i = 0; i < pixels; i++) { - const r = rgba[i * 4]; - const g = rgba[i * 4 + 1]; - const b = rgba[i * 4 + 2]; - rgb[i * 3] = r; - rgb[i * 3 + 1] = g; - rgb[i * 3 + 2] = b; - - // Luminance for brightness - const lum = 0.299 * r + 0.587 * g + 0.114 * b; - brightnessSum += lum; - - // Motion: diff from previous frame - if (this.prevFrame) { - const pr = this.prevFrame[i * 3]; - const pg = this.prevFrame[i * 3 + 1]; - const pb = this.prevFrame[i * 3 + 2]; - motionSum += Math.abs(r - pr) + Math.abs(g - pg) + Math.abs(b - pb); - } - } - - this.brightnessScore = brightnessSum / (pixels * 255); - this.motionScore = this.prevFrame ? Math.min(1, motionSum / (pixels * 100)) : 0; - this.prevFrame = new Uint8Array(rgb); - - return { - rgb, - width: targetW, - height: targetH, - motion: this.motionScore, - brightness: this.brightnessScore - }; - } - - /** - * Capture full-resolution RGBA for overlay rendering - * @returns {ImageData|null} - */ - captureFullFrame() { - if (!this.isActive) return null; - this.offscreen.width = this.width; - this.offscreen.height = this.height; - this.offCtx.drawImage(this.video, 0, 0); - return this.offCtx.getImageData(0, 0, this.width, this.height); - } - - /** - * Simple body detection from motion differencing. - * Returns approximate bounding box of moving region. - * @returns {{ x, y, w, h, detected: boolean }} - */ - detectMotionRegion(targetW = 56, targetH = 56) { - if (!this.isActive || !this.prevFrame) return { detected: false }; - - this.offscreen.width = targetW; - this.offscreen.height = targetH; - this.offCtx.drawImage(this.video, 0, 0, targetW, targetH); - const rgba = this.offCtx.getImageData(0, 0, targetW, targetH).data; - - let minX = targetW, minY = targetH, maxX = 0, maxY = 0; - let motionPixels = 0; - const threshold = 25; - - for (let y = 0; y < targetH; y++) { - for (let x = 0; x < targetW; x++) { - const i = y * targetW + x; - const r = rgba[i * 4], g = rgba[i * 4 + 1], b = rgba[i * 4 + 2]; - const pr = this.prevFrame[i * 3], pg = this.prevFrame[i * 3 + 1], pb = this.prevFrame[i * 3 + 2]; - const diff = Math.abs(r - pr) + Math.abs(g - pg) + Math.abs(b - pb); - - if (diff > threshold * 3) { - motionPixels++; - if (x < minX) minX = x; - if (y < minY) minY = y; - if (x > maxX) maxX = x; - if (y > maxY) maxY = y; - } - } - } - - const detected = motionPixels > (targetW * targetH * 0.02); - return { - detected, - x: minX / targetW, - y: minY / targetH, - w: (maxX - minX) / targetW, - h: (maxY - minY) / targetH, - coverage: motionPixels / (targetW * targetH) - }; - } -} diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json b/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json deleted file mode 100644 index f1e17faf..00000000 --- a/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "ruvector-cnn-wasm", - "type": "module", - "description": "WASM bindings for ruvector-cnn - CNN feature extraction for image embeddings", - "version": "0.1.0", - "license": "MIT OR Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/ruvnet/ruvector" - }, - "files": [ - "ruvector_cnn_wasm_bg.wasm", - "ruvector_cnn_wasm.js" - ], - "main": "ruvector_cnn_wasm.js", - "sideEffects": [ - "./snippets/*" - ], - "keywords": [ - "cnn", - "embeddings", - "wasm", - "simd", - "machine-learning" - ] -} \ No newline at end of file diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js deleted file mode 100644 index f899cf7b..00000000 --- a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js +++ /dev/null @@ -1,802 +0,0 @@ -/** - * Configuration for CNN embedder - */ -export class EmbedderConfig { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - EmbedderConfigFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_embedderconfig_free(ptr, 0); - } - constructor() { - const ret = wasm.embedderconfig_new(); - this.__wbg_ptr = ret >>> 0; - EmbedderConfigFinalization.register(this, this.__wbg_ptr, this); - return this; - } - /** - * Output embedding dimension - * @returns {number} - */ - get embedding_dim() { - const ret = wasm.__wbg_get_embedderconfig_embedding_dim(this.__wbg_ptr); - return ret >>> 0; - } - /** - * Input image size (square) - * @returns {number} - */ - get input_size() { - const ret = wasm.__wbg_get_embedderconfig_input_size(this.__wbg_ptr); - return ret >>> 0; - } - /** - * Whether to L2 normalize embeddings - * @returns {boolean} - */ - get normalize() { - const ret = wasm.__wbg_get_embedderconfig_normalize(this.__wbg_ptr); - return ret !== 0; - } - /** - * Output embedding dimension - * @param {number} arg0 - */ - set embedding_dim(arg0) { - wasm.__wbg_set_embedderconfig_embedding_dim(this.__wbg_ptr, arg0); - } - /** - * Input image size (square) - * @param {number} arg0 - */ - set input_size(arg0) { - wasm.__wbg_set_embedderconfig_input_size(this.__wbg_ptr, arg0); - } - /** - * Whether to L2 normalize embeddings - * @param {boolean} arg0 - */ - set normalize(arg0) { - wasm.__wbg_set_embedderconfig_normalize(this.__wbg_ptr, arg0); - } -} -if (Symbol.dispose) EmbedderConfig.prototype[Symbol.dispose] = EmbedderConfig.prototype.free; - -/** - * Layer operations for building custom networks - */ -export class LayerOps { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - LayerOpsFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_layerops_free(ptr, 0); - } - /** - * Apply batch normalization (returns new array) - * @param {Float32Array} input - * @param {Float32Array} gamma - * @param {Float32Array} beta - * @param {Float32Array} mean - * @param {Float32Array} _var - * @param {number} epsilon - * @returns {Float32Array} - */ - static batch_norm(input, gamma, beta, mean, _var, epsilon) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passArrayF32ToWasm0(gamma, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passArrayF32ToWasm0(beta, wasm.__wbindgen_export2); - const len2 = WASM_VECTOR_LEN; - const ptr3 = passArrayF32ToWasm0(mean, wasm.__wbindgen_export2); - const len3 = WASM_VECTOR_LEN; - const ptr4 = passArrayF32ToWasm0(_var, wasm.__wbindgen_export2); - const len4 = WASM_VECTOR_LEN; - wasm.layerops_batch_norm(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, epsilon); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var v6 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v6; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Apply global average pooling - * Returns one value per channel - * @param {Float32Array} input - * @param {number} height - * @param {number} width - * @param {number} channels - * @returns {Float32Array} - */ - static global_avg_pool(input, height, width, channels) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.layerops_global_avg_pool(retptr, ptr0, len0, height, width, channels); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var v2 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } -} -if (Symbol.dispose) LayerOps.prototype[Symbol.dispose] = LayerOps.prototype.free; - -/** - * SIMD-optimized operations - */ -export class SimdOps { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - SimdOpsFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_simdops_free(ptr, 0); - } - /** - * Dot product of two vectors - * @param {Float32Array} a - * @param {Float32Array} b - * @returns {number} - */ - static dot_product(a, b) { - const ptr0 = passArrayF32ToWasm0(a, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passArrayF32ToWasm0(b, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - const ret = wasm.simdops_dot_product(ptr0, len0, ptr1, len1); - return ret; - } - /** - * L2 normalize a vector (returns new array) - * @param {Float32Array} data - * @returns {Float32Array} - */ - static l2_normalize(data) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.simdops_l2_normalize(retptr, ptr0, len0); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var v2 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * ReLU activation (returns new array) - * @param {Float32Array} data - * @returns {Float32Array} - */ - static relu(data) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.simdops_relu(retptr, ptr0, len0); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var v2 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * ReLU6 activation (returns new array) - * @param {Float32Array} data - * @returns {Float32Array} - */ - static relu6(data) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.simdops_relu6(retptr, ptr0, len0); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var v2 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } -} -if (Symbol.dispose) SimdOps.prototype[Symbol.dispose] = SimdOps.prototype.free; - -/** - * WASM CNN Embedder for image feature extraction - */ -export class WasmCnnEmbedder { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - WasmCnnEmbedderFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_wasmcnnembedder_free(ptr, 0); - } - /** - * Compute cosine similarity between two embeddings - * @param {Float32Array} a - * @param {Float32Array} b - * @returns {number} - */ - cosine_similarity(a, b) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(a, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passArrayF32ToWasm0(b, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - wasm.wasmcnnembedder_cosine_similarity(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1); - var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - return r0; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Get the embedding dimension - * @returns {number} - */ - get embedding_dim() { - const ret = wasm.wasmcnnembedder_embedding_dim(this.__wbg_ptr); - return ret >>> 0; - } - /** - * Extract embedding from image data (RGB format, row-major) - * @param {Uint8Array} image_data - * @param {number} width - * @param {number} height - * @returns {Float32Array} - */ - extract(image_data, width, height) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArray8ToWasm0(image_data, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.wasmcnnembedder_extract(retptr, this.__wbg_ptr, ptr0, len0, width, height); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true); - if (r3) { - throw takeObject(r2); - } - var v2 = getArrayF32FromWasm0(r0, r1).slice(); - wasm.__wbindgen_export(r0, r1 * 4, 4); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Create a new CNN embedder - * @param {EmbedderConfig | null} [config] - */ - constructor(config) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - let ptr0 = 0; - if (!isLikeNone(config)) { - _assertClass(config, EmbedderConfig); - ptr0 = config.__destroy_into_raw(); - } - wasm.wasmcnnembedder_new(retptr, ptr0); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - this.__wbg_ptr = r0 >>> 0; - WasmCnnEmbedderFinalization.register(this, this.__wbg_ptr, this); - return this; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } -} -if (Symbol.dispose) WasmCnnEmbedder.prototype[Symbol.dispose] = WasmCnnEmbedder.prototype.free; - -/** - * InfoNCE loss for contrastive learning (SimCLR style) - */ -export class WasmInfoNCELoss { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - WasmInfoNCELossFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_wasminfonceloss_free(ptr, 0); - } - /** - * Compute loss for a batch of embedding pairs - * embeddings: [2N, D] flattened where (i, i+N) are positive pairs - * @param {Float32Array} embeddings - * @param {number} batch_size - * @param {number} dim - * @returns {number} - */ - forward(embeddings, batch_size, dim) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(embeddings, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - wasm.wasminfonceloss_forward(retptr, this.__wbg_ptr, ptr0, len0, batch_size, dim); - var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - return r0; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Create new InfoNCE loss with temperature parameter - * @param {number} temperature - */ - constructor(temperature) { - const ret = wasm.wasminfonceloss_new(temperature); - this.__wbg_ptr = ret >>> 0; - WasmInfoNCELossFinalization.register(this, this.__wbg_ptr, this); - return this; - } - /** - * Get the temperature parameter - * @returns {number} - */ - get temperature() { - const ret = wasm.wasminfonceloss_temperature(this.__wbg_ptr); - return ret; - } -} -if (Symbol.dispose) WasmInfoNCELoss.prototype[Symbol.dispose] = WasmInfoNCELoss.prototype.free; - -/** - * Triplet loss for metric learning - */ -export class WasmTripletLoss { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - WasmTripletLossFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_wasmtripletloss_free(ptr, 0); - } - /** - * Compute loss for a batch of triplets - * @param {Float32Array} anchors - * @param {Float32Array} positives - * @param {Float32Array} negatives - * @param {number} dim - * @returns {number} - */ - forward(anchors, positives, negatives, dim) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(anchors, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passArrayF32ToWasm0(positives, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passArrayF32ToWasm0(negatives, wasm.__wbindgen_export2); - const len2 = WASM_VECTOR_LEN; - wasm.wasmtripletloss_forward(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, dim); - var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - return r0; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Compute loss for a single triplet - * @param {Float32Array} anchor - * @param {Float32Array} positive - * @param {Float32Array} negative - * @returns {number} - */ - forward_single(anchor, positive, negative) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayF32ToWasm0(anchor, wasm.__wbindgen_export2); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passArrayF32ToWasm0(positive, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passArrayF32ToWasm0(negative, wasm.__wbindgen_export2); - const len2 = WASM_VECTOR_LEN; - wasm.wasmtripletloss_forward_single(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2); - var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); - if (r2) { - throw takeObject(r1); - } - return r0; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * Get the margin parameter - * @returns {number} - */ - get margin() { - const ret = wasm.wasmtripletloss_margin(this.__wbg_ptr); - return ret; - } - /** - * Create new triplet loss with margin - * @param {number} margin - */ - constructor(margin) { - const ret = wasm.wasmtripletloss_new(margin); - this.__wbg_ptr = ret >>> 0; - WasmTripletLossFinalization.register(this, this.__wbg_ptr, this); - return this; - } -} -if (Symbol.dispose) WasmTripletLoss.prototype[Symbol.dispose] = WasmTripletLoss.prototype.free; - -/** - * Initialize panic hook for better error messages - */ -export function init() { - wasm.init(); -} - -function __wbg_get_imports() { - const import0 = { - __proto__: null, - __wbg___wbindgen_throw_39bc967c0e5a9b58: function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }, - __wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) { - let deferred0_0; - let deferred0_1; - try { - deferred0_0 = arg0; - deferred0_1 = arg1; - console.error(getStringFromWasm0(arg0, arg1)); - } finally { - wasm.__wbindgen_export(deferred0_0, deferred0_1, 1); - } - }, - __wbg_new_227d7c05414eb861: function() { - const ret = new Error(); - return addHeapObject(ret); - }, - __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) { - const ret = getObject(arg1).stack; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export2, wasm.__wbindgen_export3); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbindgen_cast_0000000000000001: function(arg0, arg1) { - // Cast intrinsic for `Ref(String) -> Externref`. - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); - }, - __wbindgen_object_drop_ref: function(arg0) { - takeObject(arg0); - }, - }; - return { - __proto__: null, - "./ruvector_cnn_wasm_bg.js": import0, - }; -} - -const EmbedderConfigFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_embedderconfig_free(ptr >>> 0, 1)); -const LayerOpsFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_layerops_free(ptr >>> 0, 1)); -const SimdOpsFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_simdops_free(ptr >>> 0, 1)); -const WasmCnnEmbedderFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_wasmcnnembedder_free(ptr >>> 0, 1)); -const WasmInfoNCELossFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_wasminfonceloss_free(ptr >>> 0, 1)); -const WasmTripletLossFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_wasmtripletloss_free(ptr >>> 0, 1)); - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -function _assertClass(instance, klass) { - if (!(instance instanceof klass)) { - throw new Error(`expected instance of ${klass.name}`); - } -} - -function dropObject(idx) { - if (idx < 1028) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function getArrayF32FromWasm0(ptr, len) { - ptr = ptr >>> 0; - return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len); -} - -let cachedDataViewMemory0 = null; -function getDataViewMemory0() { - if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { - cachedDataViewMemory0 = new DataView(wasm.memory.buffer); - } - return cachedDataViewMemory0; -} - -let cachedFloat32ArrayMemory0 = null; -function getFloat32ArrayMemory0() { - if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) { - cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer); - } - return cachedFloat32ArrayMemory0; -} - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return decodeText(ptr, len); -} - -let cachedUint8ArrayMemory0 = null; -function getUint8ArrayMemory0() { - if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8ArrayMemory0; -} - -function getObject(idx) { return heap[idx]; } - -let heap = new Array(1024).fill(undefined); -heap.push(undefined, null, true, false); - -let heap_next = heap.length; - -function isLikeNone(x) { - return x === undefined || x === null; -} - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1, 1) >>> 0; - getUint8ArrayMemory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} - -function passArrayF32ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 4, 4) >>> 0; - getFloat32ArrayMemory0().set(arg, ptr / 4); - WASM_VECTOR_LEN = arg.length; - return ptr; -} - -function passStringToWasm0(arg, malloc, realloc) { - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8ArrayMemory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); - const ret = cachedTextEncoder.encodeInto(arg, view); - - offset += ret.written; - ptr = realloc(ptr, len, offset, 1) >>> 0; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); -cachedTextDecoder.decode(); -const MAX_SAFARI_DECODE_BYTES = 2146435072; -let numBytesDecoded = 0; -function decodeText(ptr, len) { - numBytesDecoded += len; - if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { - cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - cachedTextDecoder.decode(); - numBytesDecoded = len; - } - return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); -} - -const cachedTextEncoder = new TextEncoder(); - -if (!('encodeInto' in cachedTextEncoder)) { - cachedTextEncoder.encodeInto = function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; - }; -} - -let WASM_VECTOR_LEN = 0; - -let wasmModule, wasm; -function __wbg_finalize_init(instance, module) { - wasm = instance.exports; - wasmModule = module; - cachedDataViewMemory0 = null; - cachedFloat32ArrayMemory0 = null; - cachedUint8ArrayMemory0 = null; - wasm.__wbindgen_start(); - return wasm; -} - -async function __wbg_load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - } catch (e) { - const validResponse = module.ok && expectedResponseType(module.type); - - if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { throw e; } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - } else { - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - } else { - return instance; - } - } - - function expectedResponseType(type) { - switch (type) { - case 'basic': case 'cors': case 'default': return true; - } - return false; - } -} - -function initSync(module) { - if (wasm !== undefined) return wasm; - - - if (module !== undefined) { - if (Object.getPrototypeOf(module) === Object.prototype) { - ({module} = module) - } else { - console.warn('using deprecated parameters for `initSync()`; pass a single object instead') - } - } - - const imports = __wbg_get_imports(); - if (!(module instanceof WebAssembly.Module)) { - module = new WebAssembly.Module(module); - } - const instance = new WebAssembly.Instance(module, imports); - return __wbg_finalize_init(instance, module); -} - -async function __wbg_init(module_or_path) { - if (wasm !== undefined) return wasm; - - - if (module_or_path !== undefined) { - if (Object.getPrototypeOf(module_or_path) === Object.prototype) { - ({module_or_path} = module_or_path) - } else { - console.warn('using deprecated parameters for the initialization function; pass a single object instead') - } - } - - if (module_or_path === undefined) { - module_or_path = new URL('ruvector_cnn_wasm_bg.wasm', import.meta.url); - } - const imports = __wbg_get_imports(); - - if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { - module_or_path = fetch(module_or_path); - } - - const { instance, module } = await __wbg_load(await module_or_path, imports); - - return __wbg_finalize_init(instance, module); -} - -export { initSync, __wbg_init as default }; diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm deleted file mode 100644 index a1a54ee2..00000000 Binary files a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm and /dev/null differ