CCTV 오일 유출 감지: - GPU 추론 서버 FastAPI 서비스 (oil_inference_server.py) - Express 프록시 엔드포인트 (POST /api/aerial/oil-detect) - 프론트엔드 API 연동 (oilDetection.ts, useOilDetection.ts) - 4종 유류 클래스별 색상 오버레이 (OilDetectionOverlay.tsx) - 캡처 기능 (비디오+오버레이 합성 PNG 다운로드) - Rate limit HLS 스트리밍 skip + 한도 500 상향 HNS 대기확산: - 초기 핀 포인트 제거 (지도 클릭으로 선택) - 좌표 미선택 시 안내 메시지 표시 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
import type { OilDetectionResult, OilDetectionConfig } from '../utils/oilDetection';
|
|
import { detectOilSpillAPI, DEFAULT_OIL_DETECTION_CONFIG } from '../utils/oilDetection';
|
|
|
|
interface UseOilDetectionOptions {
|
|
videoRef: React.RefObject<HTMLVideoElement | null>;
|
|
enabled: boolean;
|
|
config?: Partial<OilDetectionConfig>;
|
|
}
|
|
|
|
interface UseOilDetectionReturn {
|
|
result: OilDetectionResult | null;
|
|
isAnalyzing: boolean;
|
|
error: string | null;
|
|
}
|
|
|
|
export function useOilDetection(options: UseOilDetectionOptions): UseOilDetectionReturn {
|
|
const { videoRef, enabled, config } = options;
|
|
|
|
const [result, setResult] = useState<OilDetectionResult | null>(null);
|
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const configRef = useRef<OilDetectionConfig>({
|
|
...DEFAULT_OIL_DETECTION_CONFIG,
|
|
...config,
|
|
});
|
|
const isBusyRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
configRef.current = {
|
|
...DEFAULT_OIL_DETECTION_CONFIG,
|
|
...config,
|
|
};
|
|
}, [config]);
|
|
|
|
const analyze = useCallback(async () => {
|
|
if (isBusyRef.current) return; // 이전 호출이 진행 중이면 스킵
|
|
|
|
const video = videoRef.current;
|
|
if (!video || video.readyState < 2) return;
|
|
|
|
isBusyRef.current = true;
|
|
setIsAnalyzing(true);
|
|
|
|
try {
|
|
const detection = await detectOilSpillAPI(video, configRef.current);
|
|
setResult(detection);
|
|
setError(null);
|
|
} catch (err) {
|
|
// API 실패 시 이전 결과 유지, 에러 메시지만 갱신
|
|
const message = err instanceof Error ? err.message : '추론 서버 연결 불가';
|
|
setError(message);
|
|
console.warn('[OilDetection] API 호출 실패:', message);
|
|
} finally {
|
|
isBusyRef.current = false;
|
|
setIsAnalyzing(false);
|
|
}
|
|
}, [videoRef]);
|
|
|
|
useEffect(() => {
|
|
if (!enabled) {
|
|
setResult(null);
|
|
setIsAnalyzing(false);
|
|
setError(null);
|
|
isBusyRef.current = false;
|
|
return;
|
|
}
|
|
|
|
setIsAnalyzing(true);
|
|
|
|
// 첫 분석: 2초 후 (영상 로딩 대기)
|
|
const firstTimeout = setTimeout(analyze, 2000);
|
|
// 반복 분석
|
|
const intervalId = setInterval(analyze, configRef.current.captureIntervalMs);
|
|
|
|
return () => {
|
|
clearTimeout(firstTimeout);
|
|
clearInterval(intervalId);
|
|
};
|
|
}, [enabled, analyze]);
|
|
|
|
return { result, isAnalyzing, error };
|
|
}
|