import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { toast } from "sonner" import { YoloDetection } from "@/features/recognition/types/yolo" import { useYoloData } from "../hooks/use-yolo-data" interface YoloDataOverlayProps { video: { id: string name: string path: string } currentTime: number } /** * Компонент для отображения данных YOLO поверх видео * Показывает рамки вокруг обнаруженных объектов и информацию о них */ export function YoloDataOverlay({ video, currentTime }: YoloDataOverlayProps) { const { t } = useTranslation() const [detections, setDetections] = useState([]) const { getYoloDataAtTimestamp } = useYoloData() // Загружаем данные YOLO для текущего времени useEffect(() => { const loadYoloData = async () => { try { // Округляем время до ближайшей секунды для оптимизации const timestamp = Math.round(currentTime) // Проверяем, есть ли данные для этого видео if (!video?.id) { setDetections([]) return } const data = await getYoloDataAtTimestamp(video.id, timestamp) setDetections(data) } catch (error) { console.error("[YoloDataOverlay] Ошибка при получении данных YOLO:", error) } } void loadYoloData() }, [video?.id, currentTime, getYoloDataAtTimestamp]) // Если нет обнаружений, не отображаем ничего if (!detections || detections.length === 0) { return null } return (
{/* Отображаем рамки для каждого обнаруженного объекта */} {detections.map((detection, index) => ( ))} {/* Информационная панель */}
{t("Обнаружено объектов")}: {detections.length}
{detections .map((d) => d.class) .filter((value, index, self) => self.indexOf(value) === index) .join(", ")}
{/* Кнопка для генерации контекста сцены для ИИ */}
) } /** * Компонент для отображения рамки вокруг обнаруженного объекта */ function DetectionBox({ detection }: { detection: YoloDetection }) { // Определяем цвет рамки в зависимости от класса объекта const getColorForClass = (className: string): string => { const colors: Record = { person: "rgba(255, 0, 0, 0.5)", car: "rgba(0, 255, 0, 0.5)", dog: "rgba(0, 0, 255, 0.5)", cat: "rgba(255, 255, 0, 0.5)", } return colors[className] || "rgba(255, 165, 0, 0.5)" } const { x, y, width, height } = detection.bbox const color = getColorForClass(detection.class) return (
{detection.class} ({Math.round(detection.confidence * 100)}%)
) } /** * Рассчитать позицию объекта в кадре * @param detection Информация об обнаруженном объекте * @returns Текстовое описание позиции */ function calculatePosition(detection: YoloDetection): string { // Рассчитываем центр объекта const centerX = detection.bbox.x + detection.bbox.width / 2 // Определяем горизонтальную позицию let horizontalPosition: string if (centerX < 0.33) { horizontalPosition = "left" } else if (centerX < 0.66) { horizontalPosition = "center" } else { horizontalPosition = "right" } // Рассчитываем вертикальную позицию const centerY = detection.bbox.y + detection.bbox.height / 2 let verticalPosition: string if (centerY < 0.33) { verticalPosition = "top" } else if (centerY < 0.66) { verticalPosition = "middle" } else { verticalPosition = "bottom" } // Возвращаем комбинированную позицию return `${verticalPosition}-${horizontalPosition}` } /** * Рассчитать размер объекта в кадре * @param detection Информация об обнаруженном объекте * @returns Текстовое описание размера */ function calculateSize(detection: YoloDetection): string { // Рассчитываем площадь объекта относительно всего кадра const area = detection.bbox.width * detection.bbox.height // Определяем размер if (area < 0.05) { return "small" } if (area < 0.15) { return "medium" } return "large" }