Inferencing perfomance

This commit is contained in:
2025-08-29 08:14:38 -06:00
parent fb2dbdf140
commit 0e2371edb9
3 changed files with 52 additions and 153 deletions

View File

@@ -19,10 +19,8 @@ type CameraStatus = 'loading' | 'active' | 'denied' | 'no_devices';
export default function HomePage() {
const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const [stream, setStream] = useState<MediaStream | null>(null);
const [cameraStatus, setCameraStatus] = useState<CameraStatus>('loading');
const [canvasCtx, setCanvasCtx] = useState<CanvasRenderingContext2D | null>(null);
const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]);
const [selectedDeviceId, setSelectedDeviceId] = useState<string>('');
@@ -40,80 +38,11 @@ export default function HomePage() {
const [shoeDetectionCount, setShoeDetectionCount] = useState(0);
const [lastSoundTime, setLastSoundTime] = useState(0);
// Draw detections function - defined early to avoid initialization order issues
const drawDetections = useCallback((
detection: DetectionResult | null,
ctx: CanvasRenderingContext2D,
video: HTMLVideoElement,
canvas: HTMLCanvasElement
) => {
console.log('🎨 drawDetections called:', { detection, videoSize: { width: video.videoWidth, height: video.videoHeight }, canvasSize: { width: canvas.width, height: canvas.height } });
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!detection) {
console.log('❌ No detection to draw');
return;
}
const videoWidth = video.videoWidth;
const videoHeight = video.videoHeight;
// Ensure canvas matches video dimensions
if (canvas.width !== videoWidth || canvas.height !== videoHeight) {
canvas.width = videoWidth;
canvas.height = videoHeight;
}
// Get bounding box coordinates [x, y, width, height]
const [x, y, width, height] = detection.bbox;
// Convert normalized coordinates to pixel coordinates
const displayX = x * videoWidth;
const displayY = y * videoHeight;
const displayWidth = width * videoWidth;
const displayHeight = height * videoHeight;
// Draw bounding box with glow effect
ctx.shadowColor = '#ff0000';
ctx.shadowBlur = 10;
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 3;
ctx.strokeRect(displayX, displayY, displayWidth, displayHeight);
// Reset shadow for text
ctx.shadowBlur = 0;
// Draw label with background
const labelText = `${detection.class} (${(detection.confidence * 100).toFixed(1)}%)`;
ctx.font = 'bold 16px Arial';
const textMetrics = ctx.measureText(labelText);
const textWidth = textMetrics.width;
const textHeight = 16;
const padding = 6;
// Label background
ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
ctx.fillRect(
displayX,
displayY > textHeight + padding ? displayY - textHeight - padding : displayY + displayHeight + 2,
textWidth + padding * 2,
textHeight + padding
);
// Label text
ctx.fillStyle = 'white';
ctx.fillText(
labelText,
displayX + padding,
displayY > textHeight + padding ? displayY - padding : displayY + displayHeight + textHeight + padding
);
}, []);
// Stable detection callback using useCallback to prevent re-registration
// Clean detection callback - no canvas drawing needed
const handleDetection = useCallback((detection: DetectionResult | null) => {
console.log('🔍 Detection callback received:', detection);
const callbackId = Math.random().toString(36).substr(2, 9);
console.log(`🔍 Detection callback received [${callbackId}]:`, detection);
setCurrentDetection(detection);
// Count actual shoe detections (not just inference attempts)
@@ -121,47 +50,49 @@ export default function HomePage() {
setShoeDetectionCount(prev => prev + 1);
}
// Get canvas context if not available
let ctx = canvasCtx;
if (canvasRef.current && !ctx) {
ctx = canvasRef.current.getContext('2d');
if (ctx) {
setCanvasCtx(ctx);
}
}
// Draw detection if we have everything we need
if (ctx && videoRef.current && canvasRef.current) {
console.log('🎨 Drawing detection on canvas');
drawDetections(detection, ctx, videoRef.current, canvasRef.current);
} else {
console.log('❌ Canvas drawing skipped - missing refs:', { canvasCtx: !!ctx, video: !!videoRef.current, canvas: !!canvasRef.current });
}
// Auto-trigger popup when shoe is detected with high confidence
if (detection && detection.confidence > 0.7) {
console.log('🎯 HIGH CONFIDENCE SHOE DETECTED! Opening popup...', detection);
console.log(`🎯 HIGH CONFIDENCE SHOE DETECTED! [${callbackId}] Opening popup...`, detection);
setPopupOpen(true);
// Play detection sound with debouncing (max once per 2 seconds)
setLastSoundTime(currentTime => {
const now = Date.now();
console.log(`🔊 Sound check [${callbackId}]: now=${now}, lastTime=${currentTime}, diff=${now - currentTime}ms`);
if (now - currentTime > 2000) {
try {
const audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuX5fLceTsFLorh8dueUA0VM6fo86JcEQ1QpO3zpWcdBSaF0fDfgToAFmq79+mbNAcZaLTp56lXFQhEnN3vuWcTBjaNz+jdjD8JEmy09+udOQcTO4nY5dqnfGUkGHS40+PvfWIuEHHf6N2AgDgGHWmy9+ibNgcZaLTn46tXFgpHot3wumwdBiqR0fDfgT4IG2u59+yfRAwKUqjn86JdEgxPndn0um0dBSyV5fLccjwGHnS89O6SQwsRYbDs46xfFgpApN7yv2clBjGN3fDbdz8JEG++7+6VNgkURY7S39KmfGQlGnS40+PoeCURYrTp46tWFwlGot3wuW0eBSqR0fDfgT8IHGy79+ibNgcZaLTo46tXFgpHot3wumwdBiqR0fDfgT4IG2u59+yfRAwKUqjn86JdEgxPndn0um0dBSyV5fLccjwGHnS89O6SQwsRYbDs46xfFgpApN7yv2clBjGN3fDbdz8JEG++7+6VNgkURY7S39KmfGQlGnS40+PoeSgIH2y69+mfRAoPUKLe8aNeAE');
console.log('🔊 Playing detection sound');
audio.play();
const audioId = Math.random().toString(36).substr(2, 9);
// Use AudioContext for more reliable single-play behavior
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
console.log(`🔊 Playing detection sound [callback:${callbackId}] [audio:${audioId}]`);
// Simple beep using Web Audio API
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.3);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.3);
console.log(`▶️ Audio beep started [${audioId}]`);
} catch (e) {
console.warn('Sound playback failed:', e);
console.warn(`Sound playback failed [${callbackId}]:`, e);
}
return now;
} else {
console.log('🔇 Sound skipped - too soon after last sound');
console.log(`🔇 Sound skipped [${callbackId}] - too soon after last sound (${now - currentTime}ms ago)`);
return currentTime;
}
});
}
}, [canvasCtx, drawDetections]);
}, []);
// Initialize ML detection system
const {
@@ -199,49 +130,30 @@ export default function HomePage() {
}
}, [stream, cameraStatus]); // Runs when stream or camera status changes
// Effect to get canvas context - ensure it's available
useEffect(() => {
if (canvasRef.current && !canvasCtx) {
const ctx = canvasRef.current.getContext('2d');
if (ctx) {
console.log('📝 Canvas context initialized');
setCanvasCtx(ctx);
}
}
}, [canvasRef, canvasCtx]);
// Track initialization state to prevent multiple attempts
const [mlInitialized, setMLInitialized] = useState(false);
// Initialize ML detection when camera is ready (only once)
useEffect(() => {
console.log('🔍 ML initialization useEffect triggered:', {
hasVideo: !!videoRef.current,
cameraStatus,
isMLLoading,
detectionEnabled,
mlInitialized
});
// Only log in development and when conditions change meaningfully
if (process.env.NODE_ENV === 'development') {
console.log('🔍 ML init check:', {
ready: videoRef.current && cameraStatus === 'active' && !isMLLoading && detectionEnabled && !mlInitialized
});
}
if (videoRef.current && cameraStatus === 'active' && !isMLLoading && detectionEnabled && !mlInitialized) {
console.log('✅ All conditions met, initializing ML detection...');
console.log('✅ Starting ML detection...');
setMLInitialized(true);
initializeML(videoRef.current).then(() => {
console.log('✅ ML detection initialized, starting continuous detection');
console.log('✅ ML ready, starting continuous detection');
startContinuous();
}).catch((error) => {
console.error('❌ Failed to initialize ML detection:', error);
console.error('❌ ML initialization failed:', error);
setMLInitialized(false); // Reset on error to allow retry
});
} else {
console.log('⏳ Waiting for conditions:', {
hasVideo: !!videoRef.current,
cameraActive: cameraStatus === 'active',
notLoading: !isMLLoading,
detectionEnabled,
alreadyInitialized: mlInitialized
});
}
}, [cameraStatus, detectionEnabled, isMLLoading, mlInitialized, initializeML, startContinuous]);
@@ -461,7 +373,6 @@ export default function HomePage() {
return (
<>
<video ref={videoRef} autoPlay playsInline muted onCanPlay={() => videoRef.current?.play()} className="h-full w-full object-cover" />
<canvas ref={canvasRef} className="absolute top-0 left-0 w-full h-full"></canvas>
{/* ML Detection Status Indicator */}
{detectionEnabled && (

View File

@@ -165,7 +165,10 @@ export class DetectionEngine {
}
this.frameSkipCounter = 0;
console.log('🔄 Running continuous detection frame...');
// Only log every 10th detection to reduce noise
if (this.metrics.detectionCount % 10 === 0) {
console.log(`🔄 Continuous detection running... (${this.metrics.detectionCount} inferences)`);
}
try {
const startTime = performance.now();

View File

@@ -119,17 +119,9 @@ async function detect(imageData: ImageData, id: string) {
);
tensor.dispose();
console.log('Worker: Model execution result:', result);
console.log('Worker: Result type:', typeof result);
console.log('Worker: Result is array:', Array.isArray(result));
if (result) {
console.log('Worker: Result length:', result.length);
if (Array.isArray(result)) {
result.forEach((item, index) => {
console.log(`Worker: Result[${index}]:`, item, 'shape:', item?.shape);
});
}
// Reduced logging for performance
if (process.env.NODE_ENV === 'development') {
console.log('Worker: Model execution completed, processing results...');
}
if (!result || !Array.isArray(result) || result.length < 4) {
@@ -146,20 +138,13 @@ async function detect(imageData: ImageData, id: string) {
const scoresData = await scores.data();
const classesData = await classes.data();
console.log('Worker: Raw model outputs:', {
boxesShape: boxes.shape,
scoresShape: scores.shape,
classesShape: classes.shape,
boxesLength: boxesData.length,
scoresLength: scoresData.length,
classesLength: classesData.length,
firstFewBoxes: Array.from(boxesData.slice(0, 8)),
firstFewScores: Array.from(scoresData.slice(0, 4)),
firstFewClasses: Array.from(classesData.slice(0, 4)),
maxScore: Math.max(...Array.from(scoresData.slice(0, 10))),
scoresAbove20: Array.from(scoresData.slice(0, 10)).filter(s => s > 0.2).length,
scoresAbove30: Array.from(scoresData.slice(0, 10)).filter(s => s > 0.3).length
});
// Only log detailed outputs when debugging specific issues
const maxScore = Math.max(...Array.from(scoresData.slice(0, 10)));
const scoresAbove30 = Array.from(scoresData.slice(0, 10)).filter(s => s > 0.3).length;
if (process.env.NODE_ENV === 'development' && (maxScore > 0.3 || scoresAbove30 > 0)) {
console.log('Worker: Potential detection found:', { maxScore, scoresAbove30 });
}
result.forEach(t => t.dispose());