Files
temp_SSA_SCAN/lib/ml/use-detection.ts
2025-09-01 11:00:23 -06:00

307 lines
9.3 KiB
TypeScript

import { useEffect, useRef, useState, useCallback } from 'react';
import type { DetectionConfig, DetectionResult, DetectionMetrics } from './types';
import { DetectionEngine } from './detection-engine';
interface UseDetectionOptions {
modelVariant?: 'quantized' | 'standard' | 'full';
enableContinuous?: boolean;
enableTrigger?: boolean;
onDetection?: (detection: DetectionResult | null) => void;
onError?: (error: Error) => void;
}
interface UseDetectionReturn {
// State
isLoading: boolean;
isDetecting: boolean;
currentDetection: DetectionResult | null;
metrics: DetectionMetrics | null;
error: string | null;
// Actions
initialize: (videoElement: HTMLVideoElement) => Promise<DetectionEngine>;
startContinuous: () => void;
stopContinuous: () => void;
triggerDetection: () => Promise<DetectionResult | null>;
updateConfig: (config: Partial<DetectionConfig>) => Promise<void>;
setDetectionCallback: (callback: (detection: DetectionResult | null) => void) => void;
// Config
config: DetectionConfig | null;
// Engine reference
detectionEngine: DetectionEngine | null;
}
/**
* React hook for shoe detection functionality
*/
export function useDetection(options: UseDetectionOptions = {}): UseDetectionReturn {
const {
modelVariant = 'standard',
enableContinuous = true,
enableTrigger = true,
onDetection,
onError
} = options;
// Store the callback in a ref so it can be updated
const detectionCallbackRef = useRef<((detection: DetectionResult | null) => void) | undefined>(onDetection);
// State
const [isLoading, setIsLoading] = useState(false);
const [isDetecting, setIsDetecting] = useState(false);
const [currentDetection, setCurrentDetection] = useState<DetectionResult | null>(null);
const [metrics, setMetrics] = useState<DetectionMetrics | null>(null);
const [error, setError] = useState<string | null>(null);
const [config, setConfig] = useState<DetectionConfig | null>(null);
// Refs
const detectionEngineRef = useRef<DetectionEngine | null>(null);
const videoElementRef = useRef<HTMLVideoElement | null>(null);
const initializationPromiseRef = useRef<Promise<void> | null>(null);
// Initialize detection engine
const initialize = useCallback(async (videoElement: HTMLVideoElement): Promise<DetectionEngine> => {
console.log('🚀 useDetection.initialize called:', { videoElement: !!videoElement });
// Prevent multiple initializations
if (initializationPromiseRef.current) {
console.log('⚠️ Initialization already in progress, returning existing promise');
return initializationPromiseRef.current;
}
setIsLoading(true);
setError(null);
const initPromise = (async () => {
try {
console.log('🏗️ Creating detection engine...');
// Create detection engine
const engine = new DetectionEngine();
detectionEngineRef.current = engine;
videoElementRef.current = videoElement;
// Set up event listeners
engine.onDetection((detection) => {
setCurrentDetection(detection);
detectionCallbackRef.current?.(detection);
});
engine.onMetrics((newMetrics) => {
setMetrics(newMetrics);
});
// Initialize with progress tracking
await engine.initialize(modelVariant, (progress) => {
// You could add progress state here if needed
console.log(`Model loading: ${progress.toFixed(1)}%`);
});
// Get initial configuration
const initialConfig = engine.getConfig();
setConfig(initialConfig);
console.log('Detection hook initialized successfully');
return engine; // Return engine instance
} catch (err) {
const error = err instanceof Error ? err : new Error('Unknown initialization error');
console.error('Detection initialization failed:', error);
setError(error.message);
onError?.(error);
throw error;
} finally {
setIsLoading(false);
}
})();
initializationPromiseRef.current = initPromise;
return initPromise;
}, [modelVariant, onDetection, onError]);
// Start continuous detection
const startContinuous = useCallback(() => {
console.log('🔄 useDetection.startContinuous called:', {
hasEngine: !!detectionEngineRef.current,
hasVideo: !!videoElementRef.current,
enableContinuous
});
if (!detectionEngineRef.current || !videoElementRef.current) {
console.warn('Detection engine or video element not available');
return;
}
if (!enableContinuous) {
console.warn('Continuous detection is disabled');
return;
}
try {
console.log('🚀 Starting continuous detection...');
detectionEngineRef.current.startContinuousDetection(videoElementRef.current);
setIsDetecting(true);
setError(null);
console.log('✅ Continuous detection started successfully');
} catch (err) {
const error = err instanceof Error ? err : new Error('Failed to start continuous detection');
console.error('❌ Start continuous detection failed:', error);
setError(error.message);
onError?.(error);
}
}, [enableContinuous, onError]);
// Stop continuous detection
const stopContinuous = useCallback(() => {
if (!detectionEngineRef.current) {
return;
}
try {
detectionEngineRef.current.stopContinuousDetection();
setIsDetecting(false);
setCurrentDetection(null);
} catch (err) {
console.error('Stop continuous detection failed:', err);
}
}, []);
// Trigger single detection
const triggerDetection = useCallback(async (): Promise<DetectionResult | null> => {
if (!detectionEngineRef.current || !videoElementRef.current) {
throw new Error('Detection engine or video element not available');
}
if (!enableTrigger) {
throw new Error('Trigger detection is disabled');
}
try {
setError(null);
const detection = await detectionEngineRef.current.triggerDetection(videoElementRef.current);
// Update current detection state
setCurrentDetection(detection);
detectionCallbackRef.current?.(detection);
return detection;
} catch (err) {
const error = err instanceof Error ? err : new Error('Trigger detection failed');
console.error('Trigger detection failed:', error);
setError(error.message);
onError?.(error);
throw error;
}
}, [enableTrigger, onError]);
// Update configuration
const updateConfig = useCallback(async (newConfig: Partial<DetectionConfig>): Promise<void> => {
if (!detectionEngineRef.current) {
throw new Error('Detection engine not available');
}
try {
await detectionEngineRef.current.updateConfig(newConfig);
const updatedConfig = detectionEngineRef.current.getConfig();
setConfig(updatedConfig);
setError(null);
} catch (err) {
const error = err instanceof Error ? err : new Error('Failed to update configuration');
console.error('Update config failed:', error);
setError(error.message);
onError?.(error);
throw error;
}
}, [onError]);
// Cleanup on unmount
useEffect(() => {
return () => {
if (detectionEngineRef.current) {
detectionEngineRef.current.destroy();
detectionEngineRef.current = null;
}
initializationPromiseRef.current = null;
videoElementRef.current = null;
};
}, []);
return {
// State
isLoading,
isDetecting,
currentDetection,
metrics,
error,
// Actions
initialize,
startContinuous,
stopContinuous,
triggerDetection,
updateConfig,
setDetectionCallback: (callback: (detection: DetectionResult | null) => void) => {
detectionCallbackRef.current = callback;
},
// Config
config,
// Engine reference
detectionEngine: detectionEngineRef.current
};
}
/**
* Hook for detection metrics monitoring
*/
export function useDetectionMetrics(detectionEngine: DetectionEngine | null) {
const [metrics, setMetrics] = useState<DetectionMetrics | null>(null);
useEffect(() => {
if (!detectionEngine) return;
detectionEngine.onMetrics(setMetrics);
// Get initial metrics
const initialMetrics = detectionEngine.getMetrics();
setMetrics(initialMetrics);
}, [detectionEngine]);
return metrics;
}
/**
* Hook for performance monitoring and adjustments
*/
export function usePerformanceOptimization(detectionEngine: DetectionEngine | null) {
const [recommendations, setRecommendations] = useState<string[]>([]);
useEffect(() => {
if (!detectionEngine) return;
const interval = setInterval(() => {
const metrics = detectionEngine.getMetrics();
const newRecommendations: string[] = [];
if (metrics.fps < 15) {
newRecommendations.push('Consider increasing frame skip or switching to a lighter model');
}
if (metrics.inferenceTime > 100) {
newRecommendations.push('Inference time is high, consider switching to quantized model');
}
if (metrics.memoryUsage > 100) {
newRecommendations.push('High memory usage detected');
}
setRecommendations(newRecommendations);
}, 5000); // Check every 5 seconds
return () => clearInterval(interval);
}, [detectionEngine]);
return recommendations;
}