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() { export default function HomePage() {
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const [stream, setStream] = useState<MediaStream | null>(null); const [stream, setStream] = useState<MediaStream | null>(null);
const [cameraStatus, setCameraStatus] = useState<CameraStatus>('loading'); const [cameraStatus, setCameraStatus] = useState<CameraStatus>('loading');
const [canvasCtx, setCanvasCtx] = useState<CanvasRenderingContext2D | null>(null);
const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]); const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]);
const [selectedDeviceId, setSelectedDeviceId] = useState<string>(''); const [selectedDeviceId, setSelectedDeviceId] = useState<string>('');
@@ -40,80 +38,11 @@ export default function HomePage() {
const [shoeDetectionCount, setShoeDetectionCount] = useState(0); const [shoeDetectionCount, setShoeDetectionCount] = useState(0);
const [lastSoundTime, setLastSoundTime] = 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; // Clean detection callback - no canvas drawing needed
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
const handleDetection = useCallback((detection: DetectionResult | null) => { 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); setCurrentDetection(detection);
// Count actual shoe detections (not just inference attempts) // Count actual shoe detections (not just inference attempts)
@@ -121,47 +50,49 @@ export default function HomePage() {
setShoeDetectionCount(prev => prev + 1); 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 // Auto-trigger popup when shoe is detected with high confidence
if (detection && detection.confidence > 0.7) { 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); setPopupOpen(true);
// Play detection sound with debouncing (max once per 2 seconds) // Play detection sound with debouncing (max once per 2 seconds)
setLastSoundTime(currentTime => { setLastSoundTime(currentTime => {
const now = Date.now(); const now = Date.now();
console.log(`🔊 Sound check [${callbackId}]: now=${now}, lastTime=${currentTime}, diff=${now - currentTime}ms`);
if (now - currentTime > 2000) { if (now - currentTime > 2000) {
try { 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'); const audioId = Math.random().toString(36).substr(2, 9);
console.log('🔊 Playing detection sound'); // Use AudioContext for more reliable single-play behavior
audio.play(); 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) { } catch (e) {
console.warn('Sound playback failed:', e); console.warn(`Sound playback failed [${callbackId}]:`, e);
} }
return now; return now;
} else { } 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; return currentTime;
} }
}); });
} }
}, [canvasCtx, drawDetections]); }, []);
// Initialize ML detection system // Initialize ML detection system
const { const {
@@ -199,49 +130,30 @@ export default function HomePage() {
} }
}, [stream, cameraStatus]); // Runs when stream or camera status changes }, [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 // Track initialization state to prevent multiple attempts
const [mlInitialized, setMLInitialized] = useState(false); const [mlInitialized, setMLInitialized] = useState(false);
// Initialize ML detection when camera is ready (only once) // Initialize ML detection when camera is ready (only once)
useEffect(() => { useEffect(() => {
console.log('🔍 ML initialization useEffect triggered:', { // Only log in development and when conditions change meaningfully
hasVideo: !!videoRef.current, if (process.env.NODE_ENV === 'development') {
cameraStatus, console.log('🔍 ML init check:', {
isMLLoading, ready: videoRef.current && cameraStatus === 'active' && !isMLLoading && detectionEnabled && !mlInitialized
detectionEnabled, });
mlInitialized }
});
if (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); setMLInitialized(true);
initializeML(videoRef.current).then(() => { initializeML(videoRef.current).then(() => {
console.log('✅ ML detection initialized, starting continuous detection'); console.log('✅ ML ready, starting continuous detection');
startContinuous(); startContinuous();
}).catch((error) => { }).catch((error) => {
console.error('❌ Failed to initialize ML detection:', error); console.error('❌ ML initialization failed:', error);
setMLInitialized(false); // Reset on error to allow retry 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]); }, [cameraStatus, detectionEnabled, isMLLoading, mlInitialized, initializeML, startContinuous]);
@@ -461,7 +373,6 @@ export default function HomePage() {
return ( return (
<> <>
<video ref={videoRef} autoPlay playsInline muted onCanPlay={() => videoRef.current?.play()} className="h-full w-full object-cover" /> <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 */} {/* ML Detection Status Indicator */}
{detectionEnabled && ( {detectionEnabled && (

View File

@@ -165,7 +165,10 @@ export class DetectionEngine {
} }
this.frameSkipCounter = 0; 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 { try {
const startTime = performance.now(); const startTime = performance.now();

View File

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