// 科技感视觉识别系统 class VisionSystem { constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); this.overlay = document.createElement('div'); this.overlay.className = 'vision-overlay'; this.canvas.parentElement.appendChild(this.overlay); // 状态 this.mode = 'SEGMENT'; this.fps = 0; this.detectedObjects = []; this.handData = null; this.trackingData = null; // 初始化UI元素 this.initUI(); // 连接WebSocket this.connectVisionWS(); } initUI() { // 状态指示器 this.statusElement = this.createStatusIndicator(); this.overlay.appendChild(this.statusElement); // 进度条 this.progressElement = this.createProgressBars(); this.overlay.appendChild(this.progressElement); // 数据面板 this.dataPanel = this.createDataPanel(); this.overlay.appendChild(this.dataPanel); } createStatusIndicator() { const status = document.createElement('div'); status.className = 'status-indicator'; status.innerHTML = `
系统就绪 System Ready
等待目标 Waiting for Target
`; return status; } createProgressBars() { const container = document.createElement('div'); container.className = 'progress-container'; container.innerHTML = `
对齐度 Alignment 0%
距离匹配 Distance Match 0%
`; return container; } createDataPanel() { const panel = document.createElement('div'); panel.className = 'data-panel'; panel.innerHTML = `
FPS --
模式 Mode 检测
目标数 Objects 0
握持分 Grasp 0.00
`; return panel; } connectVisionWS() { const proto = location.protocol === 'https:' ? 'wss' : 'ws'; this.ws = new WebSocket(`${proto}://${location.host}/ws/viewer`); // 改为 /ws/viewer this.ws.onopen = () => { console.log('[Vision] WebSocket connected'); // ... rest of the code }; this.ws.onmessage = (event) => { // 处理二进制图像数据 if (event.data instanceof Blob) { // 创建图像URL并显示 const url = URL.createObjectURL(event.data); const img = new Image(); img.onload = () => { this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height); URL.revokeObjectURL(url); }; img.src = url; } }; this.ws.onerror = () => { console.error('Vision WebSocket error'); }; } updateVisualization(data) { // 更新状态 this.mode = data.mode || 'SEGMENT'; this.fps = data.fps || 0; // 更新UI this.updateStatus(data); this.updateProgress(data); this.updateDataPanel(data); // 绘制可视化 if (data.frame) { this.drawFrame(data.frame); } if (data.hand) { this.drawHand(data.hand); } if (data.objects) { this.drawObjects(data.objects); } if (data.tracking) { this.drawTracking(data.tracking); } } updateStatus(data) { const statusMain = this.statusElement.querySelector('.status-main'); const statusSub = this.statusElement.querySelector('.status-sub:last-child'); switch(this.mode) { case 'SEGMENT': statusMain.innerHTML = '目标检测中 Detecting'; statusSub.textContent = data.message || '扫描环境 Scanning Environment'; break; case 'FLASH': statusMain.innerHTML = '锁定中 Locking'; statusSub.textContent = '准备追踪 Preparing to Track'; break; case 'TRACK': statusMain.innerHTML = '追踪中 Tracking'; statusSub.textContent = '保持对准 Maintain Alignment'; break; } } updateProgress(data) { if (data.alignScore !== undefined) { const alignPercent = Math.round(data.alignScore * 100); document.getElementById('align-progress').style.width = `${alignPercent}%`; this.progressElement.querySelector('.progress-value').textContent = `${alignPercent}%`; } if (data.distanceScore !== undefined) { const distPercent = Math.round(data.distanceScore * 100); document.getElementById('distance-progress').style.width = `${distPercent}%`; this.progressElement.querySelectorAll('.progress-value')[1].textContent = `${distPercent}%`; } } updateDataPanel(data) { document.getElementById('fps-value').textContent = Math.round(this.fps); document.getElementById('mode-value').textContent = this.getModeText(this.mode); document.getElementById('objects-value').textContent = data.objectCount || 0; document.getElementById('grasp-value').textContent = (data.graspScore || 0).toFixed(2); } getModeText(mode) { const modeMap = { 'SEGMENT': '检测 Detect', 'FLASH': '锁定 Lock', 'TRACK': '追踪 Track' }; return modeMap[mode] || mode; } drawFrame(frameData) { // 绘制基础图像 const img = new Image(); img.onload = () => { this.canvas.width = img.width; this.canvas.height = img.height; this.ctx.drawImage(img, 0, 0); }; img.src = 'data:image/jpeg;base64,' + frameData; } drawHand(handData) { // 使用SVG绘制手部骨骼 const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.style.position = 'absolute'; svg.style.top = '0'; svg.style.left = '0'; svg.style.width = '100%'; svg.style.height = '100%'; svg.style.pointerEvents = 'none'; // 绘制连接线 handData.connections.forEach(conn => { const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', conn.start.x); line.setAttribute('y1', conn.start.y); line.setAttribute('x2', conn.end.x); line.setAttribute('y2', conn.end.y); line.setAttribute('class', 'hand-skeleton'); svg.appendChild(line); }); // 绘制关节点 handData.landmarks.forEach(point => { const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', point.x); circle.setAttribute('cy', point.y); circle.setAttribute('r', '3'); circle.setAttribute('class', 'hand-joint'); svg.appendChild(circle); }); // 添加到覆盖层 const oldSvg = this.overlay.querySelector('svg'); if (oldSvg) oldSvg.remove(); this.overlay.appendChild(svg); } drawObjects(objects) { // 绘制检测到的物体 objects.forEach((obj, index) => { if (obj.isTarget) { // 目标物体用特殊样式 this.drawTargetObject(obj); } else { // 其他物体用普通样式 this.drawNormalObject(obj); } }); } drawTargetObject(obj) { // 创建目标锁定效果 const target = document.createElement('div'); target.className = 'target-lock'; target.style.position = 'absolute'; target.style.left = `${obj.x}px`; target.style.top = `${obj.y}px`; target.style.width = `${obj.width}px`; target.style.height = `${obj.height}px`; // 添加锁定动画 const svg = ` `; target.innerHTML = svg; this.overlay.appendChild(target); } } // 初始化 document.addEventListener('DOMContentLoaded', () => { const visionSystem = new VisionSystem('vision-canvas'); });