Inferencing perfomance
This commit is contained in:
165
app/page.tsx
165
app/page.tsx
@@ -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 && (
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user