From a3604f6d3639897fcce4f722185441bbc922462c Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Tue, 6 May 2025 20:03:09 +0300 Subject: [PATCH 1/4] add type to detections --- .../AnnotationMain/AnnotationMain.js | 12 +++++-- src/components/CanvasEditor/CanvasEditor.js | 4 ++- src/components/Detection.js | 12 +++---- .../DetectionClassList/DetectionClassList.css | 32 +++++++++++++++++++ .../DetectionClassList/DetectionClassList.js | 32 ++++++++++++++++++- src/components/DetectionContainer.js | 4 ++- src/constants/detectionTypes.js | 5 +++ src/services/AnnotationService.js | 17 +++++----- 8 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 src/constants/detectionTypes.js diff --git a/src/components/AnnotationMain/AnnotationMain.js b/src/components/AnnotationMain/AnnotationMain.js index c4cb491..fa310e4 100644 --- a/src/components/AnnotationMain/AnnotationMain.js +++ b/src/components/AnnotationMain/AnnotationMain.js @@ -8,6 +8,7 @@ import * as AnnotationService from '../../services/AnnotationService'; import AnnotationControls from '../AnnotationControls/AnnotationControls'; import saveAnnotation from '../../services/DataHandler'; import './AnnotationMain.css'; +import { detectionTypes } from '../../constants/detectionTypes'; function AnnotationMain() { const [files, setFiles] = useState([]); @@ -21,6 +22,7 @@ function AnnotationMain() { const [videoWidth, setVideoWidth] = useState(640); const [videoHeight, setVideoHeight] = useState(480); const [errorMessage, setErrorMessage] = useState(""); + const [detectionType, setDetectionType] = useState(detectionTypes.day) const videoRef = useRef(null); const containerRef = useRef(null); @@ -71,7 +73,8 @@ function AnnotationMain() { const imageData = AnnotationService.createAnnotationImage( videoRef, detections, - safeContainerRef + safeContainerRef, + detectionType ); if (imageData) { @@ -186,7 +189,11 @@ function AnnotationMain() { onDropNewFiles={handleDropNewFiles} /> - +
@@ -214,6 +221,7 @@ function AnnotationMain() { onDetectionsChange={handleDetectionsChange} onSelectionChange={handleSelectionChange} detectionClass={selectedClass} + detectionType={detectionType} />
diff --git a/src/components/CanvasEditor/CanvasEditor.js b/src/components/CanvasEditor/CanvasEditor.js index 6f02cc9..d7527fc 100644 --- a/src/components/CanvasEditor/CanvasEditor.js +++ b/src/components/CanvasEditor/CanvasEditor.js @@ -12,7 +12,8 @@ function CanvasEditor({ onDetectionsChange, onSelectionChange, children, - detectionClass + detectionClass, + detectionType }) { const containerRef = useRef(null); const [currentDetection, setCurrentDetection] = useState(initialCurrentDetection); @@ -258,6 +259,7 @@ function CanvasEditor({ onDetectionMouseDown={handleDetectionMouseDown} currentDetection={currentDetection} onResize={handleResize} + detectionType={detectionType} /> diff --git a/src/components/Detection.js b/src/components/Detection.js index eb98433..c1e0f1f 100644 --- a/src/components/Detection.js +++ b/src/components/Detection.js @@ -1,6 +1,7 @@ import React from 'react'; +import { detectionTypes } from '../constants/detectionTypes'; -function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) { +function Detection({ detection, isSelected, onDetectionMouseDown, onResize, detectionType }) { if (!detection || !detection.class) { return null; } @@ -13,10 +14,6 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) { } // Use startsWith to correctly handle RGBA and hex colors - const backgroundColor = color.startsWith('rgba') - ? color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 0.4)') - : color.replace(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/, 'rgba($1, $2, $3, 0.4)'); - const borderColor = color.startsWith('rgba') ? color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 1)') : color; @@ -40,7 +37,6 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) { top: `${detection.y1}px`, width: `${detection.x2 - detection.x1}px`, height: `${detection.y2 - detection.y1}px`, - backgroundColor: backgroundColor, border: `2px solid ${borderColor}`, boxSizing: 'border-box', cursor: isSelected ? 'move' : 'default', @@ -50,7 +46,7 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) { if (isSelected) { style.border = `3px solid black`; - style.boxShadow = `0 0 4px 2px ${borderColor}`; + style.boxShadow = `0 0 4px 4px ${borderColor}`; } const handleMouseDown = (e) => { @@ -92,7 +88,7 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) { textShadow: '1px 1px 2px black', pointerEvents: 'none' }}> - {detection.class.Name} + {detection.class.Name} {detectionType !== detectionTypes.day && '(' + detectionType + ')'} ); diff --git a/src/components/DetectionClassList/DetectionClassList.css b/src/components/DetectionClassList/DetectionClassList.css index 562f708..6d0320c 100644 --- a/src/components/DetectionClassList/DetectionClassList.css +++ b/src/components/DetectionClassList/DetectionClassList.css @@ -21,4 +21,36 @@ font-weight: 500; margin-bottom: 2px; border-radius: 4px; +} + +.detection-type-group { + display: flex; + flex-direction: row; + justify-content: center; + gap: 5%; + padding: 10px 12px; +} + +.detection-type-btn { + width: 25%; + display: flex; + justify-content: center; + align-items: center; + font-size: 18px; + padding: 4px 10px; + border-radius: 4px; +} + +.detection-type-btn:hover { + background: #d6eed5; +} + +.active-type { + color: white; + background: black; +} + +.active-type:hover { + cursor: default; + background: black; } \ No newline at end of file diff --git a/src/components/DetectionClassList/DetectionClassList.js b/src/components/DetectionClassList/DetectionClassList.js index a56dd54..c45cabd 100644 --- a/src/components/DetectionClassList/DetectionClassList.js +++ b/src/components/DetectionClassList/DetectionClassList.js @@ -1,8 +1,11 @@ import React, { useEffect, useState } from 'react'; import DetectionClass from '../../models/DetectionClass'; import './DetectionClassList.css'; +import { MdOutlineNightlightRound, MdOutlineWbSunny } from "react-icons/md"; +import { FaRegSnowflake } from 'react-icons/fa'; +import { detectionTypes } from '../../constants/detectionTypes'; -function DetectionClassList({ onClassSelect }) { +function DetectionClassList({ onClassSelect, detectionType, setDetectionType }) { const [detectionClasses, setDetectionClasses] = useState([]); const [selectedClass, setSelectedClass] = useState(null); @@ -83,6 +86,10 @@ function DetectionClassList({ onClassSelect }) { onClassSelect && onClassSelect(cls); }; + const handleTypeClick = (type) => { + setDetectionType(type); + } + return (

Classes

@@ -107,6 +114,29 @@ function DetectionClassList({ onClassSelect }) { ); })} + +
+ + + + + +
); } diff --git a/src/components/DetectionContainer.js b/src/components/DetectionContainer.js index 8d20b82..1c64e1c 100644 --- a/src/components/DetectionContainer.js +++ b/src/components/DetectionContainer.js @@ -2,7 +2,7 @@ import React from 'react'; import Detection from './Detection'; -function DetectionContainer({ detections, selectedDetectionIndices, onDetectionMouseDown, currentDetection, onResize }) { +function DetectionContainer({ detections, selectedDetectionIndices, onDetectionMouseDown, currentDetection, onResize, detectionType }) { return ( <> @@ -13,6 +13,7 @@ function DetectionContainer({ detections, selectedDetectionIndices, onDetectionM isSelected={selectedDetectionIndices.includes(index)} onDetectionMouseDown={(e) => onDetectionMouseDown(e, index)} onResize={(e, position) => onResize(e, index, position)} + detectionType={detectionType} /> ))} {currentDetection && ( @@ -21,6 +22,7 @@ function DetectionContainer({ detections, selectedDetectionIndices, onDetectionM isSelected={false} onDetectionMouseDown={() => {}} // No-op handler for the current detection onResize={() => {}} // No-op handler for the current detection + detectionType={detectionType} /> )} diff --git a/src/constants/detectionTypes.js b/src/constants/detectionTypes.js new file mode 100644 index 0000000..f5625c5 --- /dev/null +++ b/src/constants/detectionTypes.js @@ -0,0 +1,5 @@ +export const detectionTypes = { + day: 'day', + night: 'night', + winter: 'winter' +} \ No newline at end of file diff --git a/src/services/AnnotationService.js b/src/services/AnnotationService.js index 594205e..0a6c90d 100644 --- a/src/services/AnnotationService.js +++ b/src/services/AnnotationService.js @@ -1,3 +1,5 @@ +import { detectionTypes } from "../constants/detectionTypes"; + export const calculateRelativeCoordinates = (e, containerRef) => { if (!containerRef.current) return { x: 0, y: 0 }; const containerRect = containerRef.current.getBoundingClientRect(); @@ -13,7 +15,7 @@ export const isMouseOverDetection = (x, y, detection, containerRef) => { return relativeX >= detection.x1 && relativeX <= detection.x2 && relativeY >= detection.y1 && relativeY <= detection.y2; }; -export const createAnnotationImage = (videoRef, detections, containerRef) => { +export const createAnnotationImage = (videoRef, detections, containerRef, detectionType) => { if (!videoRef?.current || !containerRef?.current) { console.warn("Missing video or container reference"); return null; @@ -39,17 +41,12 @@ export const createAnnotationImage = (videoRef, detections, containerRef) => { detections.forEach(detection => { if (!detection?.class) return; - // Ensure proper opacity for background - consistently using 0.4 opacity - const bgColor = detection.class.Color?.startsWith('rgba') - ? detection.class.Color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 0.4)') - : detection.class.Color?.replace(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/, 'rgba($1, $2, $3, 0.4)') || 'rgba(255, 0, 0, 0.4)'; - // Ensure full opacity for border const borderColor = detection.class.Color?.startsWith('rgba') ? detection.class.Color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 1)') : detection.class.Color || 'rgba(255, 0, 0, 1)'; - ctx.fillStyle = bgColor; + ctx.fillStyle = 'rgba(255, 255, 255, 0)'; ctx.strokeStyle = borderColor; const x = Math.max(0, detection.x1 || 0) * detection.kw; @@ -63,7 +60,11 @@ export const createAnnotationImage = (videoRef, detections, containerRef) => { ctx.fillStyle = 'white'; ctx.font = '12px Arial'; - ctx.fillText(detection.class.Name || 'Unknown', x, y - 5); + if (detectionType === detectionTypes.day){ + ctx.fillText(detection.class.Name || 'Unknown', x, y - 5); + } else { + ctx.fillText(`${detection.class.Name} (${detectionType}) `|| 'Unknown', x, y - 5); + } }); } From 091130840f43373de377ff1dd60a4edb3a0a0157 Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Tue, 6 May 2025 20:31:40 +0300 Subject: [PATCH 2/4] add button to open files and folders --- src/components/MediaList/MediaList.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/MediaList/MediaList.js b/src/components/MediaList/MediaList.js index 07e77cd..53e3ebd 100644 --- a/src/components/MediaList/MediaList.js +++ b/src/components/MediaList/MediaList.js @@ -3,16 +3,23 @@ import { useDropzone } from 'react-dropzone'; import './MediaList.css' function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) { - const { getRootProps, getInputProps, isDragActive } = useDropzone({ + const { getRootProps, getInputProps, isDragActive, open: openFileDialog } = useDropzone({ onDrop: onDropNewFiles, + multiple: true, }); + const { getRootProps: getFolderRootProps, getInputProps: getFolderInputProps, open: openFolderDialog } = useDropzone({ + onDrop: onDropNewFiles, + multiple: true + }); + + const [filteredFiles, setFilteredFiles] = useState(files); - useEffect(()=>{ + useEffect(() => { setFilteredFiles(files); - },[files]) + }, [files]) + - const handleInputChange = (e) => { const value = e.target.value; const filtered = files.filter((file) => file.name.toLowerCase().includes(value.toLowerCase())); @@ -22,7 +29,13 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) { return (

Files

- + + +
    {filteredFiles.map((file) => (
  • +
    + +
    {isDragActive ? (

    Drop here

    ) : ( From 2c6b9a882276b8ef0a613910d1322ec5682b54bb Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Sat, 28 Jun 2025 14:46:59 +0300 Subject: [PATCH 3/4] redesign --- .../AnnotationControls/AnnotationControls.css | 68 ++++++++----------- .../AnnotationControls/AnnotationControls.js | 32 +++++---- .../AnnotationList/AnnotationList.css | 28 ++++++++ .../{ => AnnotationList}/AnnotationList.js | 10 ++- .../AnnotationMain/AnnotationMain.css | 17 ++--- .../AnnotationMain/AnnotationMain.js | 2 +- .../DetectionClassList/DetectionClassList.css | 66 ++++++++++++------ .../DetectionClassList/DetectionClassList.js | 47 +++++++------ src/components/MediaList/MediaList.css | 68 ++++++++++++++++--- src/components/MediaList/MediaList.js | 18 ++--- src/icons/CleanIcon.js | 7 ++ src/icons/DeleteIcon.js | 8 +++ src/icons/NextIcon.js | 8 +++ src/icons/PauseIcon.js | 8 +++ src/icons/PlayIcon.js | 7 ++ src/icons/PreviousIcon.js | 8 +++ src/icons/SaveIcon.js | 7 ++ src/icons/StopIcon.js | 7 ++ src/index.css | 4 ++ 19 files changed, 293 insertions(+), 127 deletions(-) create mode 100644 src/components/AnnotationList/AnnotationList.css rename src/components/{ => AnnotationList}/AnnotationList.js (61%) create mode 100644 src/icons/CleanIcon.js create mode 100644 src/icons/DeleteIcon.js create mode 100644 src/icons/NextIcon.js create mode 100644 src/icons/PauseIcon.js create mode 100644 src/icons/PlayIcon.js create mode 100644 src/icons/PreviousIcon.js create mode 100644 src/icons/SaveIcon.js create mode 100644 src/icons/StopIcon.js diff --git a/src/components/AnnotationControls/AnnotationControls.css b/src/components/AnnotationControls/AnnotationControls.css index d09d26b..8da947c 100644 --- a/src/components/AnnotationControls/AnnotationControls.css +++ b/src/components/AnnotationControls/AnnotationControls.css @@ -1,61 +1,47 @@ +.controls { + margin-top: 4px; +} + .input-group { display: flex; flex-direction: row; - padding: 0 4px; + background: #222531; + padding: 0 20px; + border-radius: 4px; +} + +.time { + color: #fff; } .video-slider { - width: 100%; - margin: 12px 20px; + margin: 12px 26px; +} + +.MuiSlider-root { + color: #fff !important; } .buttons-group { display: flex; - justify-content: center; - position: relative; - z-index: 1; - padding: 10px; - background: #f5f5f5; - border-radius: 4px; - margin-top: 10px; + flex-direction: row; + gap: 10px; + margin-top: 6px; } .control-btn { + width: 40px; + height: 40px; display: flex; + justify-content: center; align-items: center; - gap: 4px; - padding: 10px 20px; - font-size: 16px; - margin: 0 5px; - border: 1px solid #ccc; + background: #222531; + padding: 4px; + border: 0; border-radius: 4px; cursor: pointer; } -.play-btn { - background: #8aff8a; -} - -.pause-btn { - background: #ff8a8a; -} - -.arrow-btn { - background: #e0e0e0; -} - -.stop-btn { - background: #e8a1a5; -} - -.save-btn { - background: #8ad4ff; -} - -.delete-btn { - background: #ffb38a; -} - -.clean-btn{ - background: #ff8a8a; +.control-btn:hover { + background: #535b77; } \ No newline at end of file diff --git a/src/components/AnnotationControls/AnnotationControls.js b/src/components/AnnotationControls/AnnotationControls.js index 40f5b46..4e5363c 100644 --- a/src/components/AnnotationControls/AnnotationControls.js +++ b/src/components/AnnotationControls/AnnotationControls.js @@ -1,7 +1,13 @@ import { Slider } from '@mui/material'; -import { FaStop } from "react-icons/fa"; -import { MdCleaningServices } from "react-icons/md"; import './AnnotationControls.css'; +import PreviousIcon from '../../icons/PreviousIcon'; +import PlayIcon from '../../icons/PlayIcon'; +import PauseIcon from '../../icons/PauseIcon'; +import NextIcon from '../../icons/NextIcon'; +import StopIcon from '../../icons/StopIcon'; +import SaveIcon from '../../icons/SaveIcon'; +import CleanIcon from '../../icons/CleanIcon'; +import DeleteIcon from '../../icons/DeleteIcon'; function AnnotationControls({ videoRef, @@ -32,7 +38,7 @@ function AnnotationControls({ return (
    -

    {formatDuration(currentTime)}

    +

    {formatDuration(currentTime)}

    {videoRef.current !== null - ?

    {formatDuration(videoRef.current.duration - currentTime)}

    - :

    {formatDuration(0)}

    + ?

    {formatDuration(videoRef.current.duration - currentTime)}

    + :

    {formatDuration(0)}

    }
    @@ -55,46 +61,48 @@ function AnnotationControls({ onClick={onFrameBackward} title="Previous Frame" > - ⏮️ +
    diff --git a/src/components/AnnotationList/AnnotationList.css b/src/components/AnnotationList/AnnotationList.css new file mode 100644 index 0000000..c5091be --- /dev/null +++ b/src/components/AnnotationList/AnnotationList.css @@ -0,0 +1,28 @@ +.annotation-section { + background: #222531; + border-radius: 4px; + padding: 8px; + height: 80%; +} + +.annotation-list { + display: flex; + flex-direction: column; + gap: 4px; + list-style-type: none; + padding: 0; +} + +.annotation-list-item { + box-sizing: border-box; + display: flex; + align-items: center; + height: 22px; + background: #858CA2; + padding: 4px; + color: #fff; + font-size: 14px; + line-height: 1; + font-weight: 600; + border-radius: 4px ; +} \ No newline at end of file diff --git a/src/components/AnnotationList.js b/src/components/AnnotationList/AnnotationList.js similarity index 61% rename from src/components/AnnotationList.js rename to src/components/AnnotationList/AnnotationList.js index 241964f..0430627 100644 --- a/src/components/AnnotationList.js +++ b/src/components/AnnotationList/AnnotationList.js @@ -1,14 +1,12 @@ -// src/components/AnnotationList.js -// No changes -import React from 'react'; +import './AnnotationList.css' function AnnotationList({ annotations, onAnnotationClick }) { return ( -
    +

    Annotations

    -
      +
        {annotations.map((annotation, index) => ( -
      • onAnnotationClick(annotation.time)}> +
      • onAnnotationClick(annotation.time)}> Frame {index + 1} - {annotation.annotations.length} objects
      • ))} diff --git a/src/components/AnnotationMain/AnnotationMain.css b/src/components/AnnotationMain/AnnotationMain.css index 8c28be9..87005d5 100644 --- a/src/components/AnnotationMain/AnnotationMain.css +++ b/src/components/AnnotationMain/AnnotationMain.css @@ -1,33 +1,32 @@ .content-wrapper { display: flex; + gap: 4px; height: 100vh; overflow: hidden; + background: #0D1421; + padding: 4px; } .side-menu { display: flex; flex-direction: column; - width: 15%; - height: 100%; -} - -.left-menu{ - border-right: 1px solid #ccc; + width: 228px; + height: 100vh; } .right-menu{ overflow-y: auto; - border-left: 1px solid #ccc; } .player-wrapper { - width: 70%; + width: calc(100% - 464px); height: 100%; display: flex; flex-direction: column; } .error-message { + position: absolute; background: #ffdddd; color: #d8000c; padding: 6px; @@ -51,4 +50,6 @@ justify-content: center; align-items: center; overflow: hidden; + border-radius: 8px; + border: 1px solid #222531; } \ No newline at end of file diff --git a/src/components/AnnotationMain/AnnotationMain.js b/src/components/AnnotationMain/AnnotationMain.js index fa310e4..df9b849 100644 --- a/src/components/AnnotationMain/AnnotationMain.js +++ b/src/components/AnnotationMain/AnnotationMain.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; import VideoPlayer from '../VideoPlayer/VideoPlayer'; -import AnnotationList from '../AnnotationList'; +import AnnotationList from '../AnnotationList/AnnotationList'; import MediaList from '../MediaList/MediaList'; import DetectionClassList from '../DetectionClassList/DetectionClassList'; import CanvasEditor from '../CanvasEditor/CanvasEditor'; diff --git a/src/components/DetectionClassList/DetectionClassList.css b/src/components/DetectionClassList/DetectionClassList.css index 6d0320c..0b642bb 100644 --- a/src/components/DetectionClassList/DetectionClassList.css +++ b/src/components/DetectionClassList/DetectionClassList.css @@ -1,56 +1,80 @@ +.detection { + margin-top: 4px; +} + .class-list { - flex-grow: 1; + display: flex; + flex-direction: column; + background: #858CA2; + border-radius: 4px; + padding: 4px; + height: 48vh; +} + +.menu-title { + margin-bottom: 6px; } .class-list-group { - list-style-type: none; + display: flex; + flex-direction: column; + gap: 3px; padding: 0; margin: 0; - height: 40vh; - min-height: 300px; overflow: auto; + scrollbar-width: none; + list-style-type: none; +} + +.class-list-group::-webkit-scrollbar { + display: none; } .class-list-item { display: flex; align-items: center; - height: 12px; + height: 30px; cursor: pointer; padding: 8px; font-size: 14px; font-weight: 500; - margin-bottom: 2px; border-radius: 4px; } .detection-type-group { + background: #222531; display: flex; flex-direction: row; - justify-content: center; - gap: 5%; - padding: 10px 12px; -} - -.detection-type-btn { - width: 25%; - display: flex; - justify-content: center; - align-items: center; - font-size: 18px; - padding: 4px 10px; + justify-content: space-between; + padding: 16px 9px; + margin-top: 4px; border-radius: 4px; } +.detection-type-btn { + width: 66px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + background: #3862fb41; + color: #3861FB; + font-size: 30px; + padding: 5px 17px; + border-radius: 4px; + border: 0; +} + .detection-type-btn:hover { - background: #d6eed5; + background: #0e2060; } .active-type { color: white; - background: black; + background: #3861FB; } .active-type:hover { cursor: default; - background: black; + background: #3861FB; } \ No newline at end of file diff --git a/src/components/DetectionClassList/DetectionClassList.js b/src/components/DetectionClassList/DetectionClassList.js index c45cabd..a8d68a0 100644 --- a/src/components/DetectionClassList/DetectionClassList.js +++ b/src/components/DetectionClassList/DetectionClassList.js @@ -91,29 +91,32 @@ function DetectionClassList({ onClassSelect, detectionType, setDetectionType }) } return ( -
        -

        Classes

        -
          - {detectionClasses.map((cls) => { - const backgroundColor = calculateColor(cls.Id); - const darkBg = calculateColor(cls.Id, '0.8'); - const isSelected = selectedClass && selectedClass.Id === cls.Id; +
          - return ( -
        • handleClassClick(cls)} - > - {cls.Name} -
        • - ); - })} -
        +
        +

        Classes

        +
          + {detectionClasses.map((cls) => { + const backgroundColor = calculateColor(cls.Id); + const darkBg = calculateColor(cls.Id, '0.8'); + const isSelected = selectedClass && selectedClass.Id === cls.Id; + + return ( +
        • handleClassClick(cls)} + > + {cls.Name} +
        • + ); + })} +
        +
        - +
        +

        Files

        + + +
          {filteredFiles.map((file) => ( @@ -42,7 +44,7 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) { className='file-list-item' key={file.name} style={{ - backgroundColor: selectedFile === file ? '#f0f0f0' : 'transparent' + backgroundColor: selectedFile === file ? '#474A52' : '#858CA2' }} onClick={() => onFileSelect(file)} > diff --git a/src/icons/CleanIcon.js b/src/icons/CleanIcon.js new file mode 100644 index 0000000..05e34ec --- /dev/null +++ b/src/icons/CleanIcon.js @@ -0,0 +1,7 @@ +const CleanIcon = ({ width = 32, height = 32 }) => ( + + + +); + +export default CleanIcon; \ No newline at end of file diff --git a/src/icons/DeleteIcon.js b/src/icons/DeleteIcon.js new file mode 100644 index 0000000..a78d67a --- /dev/null +++ b/src/icons/DeleteIcon.js @@ -0,0 +1,8 @@ +const DeleteIcon = ({ width = 32, height = 32 }) => ( + + + + +); + +export default DeleteIcon; \ No newline at end of file diff --git a/src/icons/NextIcon.js b/src/icons/NextIcon.js new file mode 100644 index 0000000..4c1aa02 --- /dev/null +++ b/src/icons/NextIcon.js @@ -0,0 +1,8 @@ +const NextIcon = ({ width = 32, height = 32 }) => ( + + + + +); + +export default NextIcon; \ No newline at end of file diff --git a/src/icons/PauseIcon.js b/src/icons/PauseIcon.js new file mode 100644 index 0000000..7d8bd7d --- /dev/null +++ b/src/icons/PauseIcon.js @@ -0,0 +1,8 @@ +const PauseIcon = ({ width = 32, height = 32 }) => ( + + + + +); + +export default PauseIcon; \ No newline at end of file diff --git a/src/icons/PlayIcon.js b/src/icons/PlayIcon.js new file mode 100644 index 0000000..cf9e9b5 --- /dev/null +++ b/src/icons/PlayIcon.js @@ -0,0 +1,7 @@ +const PlayIcon = ({ width = 32, height = 32 }) => ( + + + +); + +export default PlayIcon; \ No newline at end of file diff --git a/src/icons/PreviousIcon.js b/src/icons/PreviousIcon.js new file mode 100644 index 0000000..b1a1a29 --- /dev/null +++ b/src/icons/PreviousIcon.js @@ -0,0 +1,8 @@ +const PreviousIcon = ({ width = 32, height = 32 }) => ( + + + + +); + +export default PreviousIcon; \ No newline at end of file diff --git a/src/icons/SaveIcon.js b/src/icons/SaveIcon.js new file mode 100644 index 0000000..7726492 --- /dev/null +++ b/src/icons/SaveIcon.js @@ -0,0 +1,7 @@ +const SaveIcon = ({ width = 32, height = 32 }) => ( + + + +); + +export default SaveIcon; \ No newline at end of file diff --git a/src/icons/StopIcon.js b/src/icons/StopIcon.js new file mode 100644 index 0000000..ab4884c --- /dev/null +++ b/src/icons/StopIcon.js @@ -0,0 +1,7 @@ +const StopIcon = ({ width = 32, height = 32 }) => ( + + + +); + +export default StopIcon; \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e..4551eb2 100644 --- a/src/index.css +++ b/src/index.css @@ -7,6 +7,10 @@ body { -moz-osx-font-smoothing: grayscale; } +* { + box-sizing: border-box; +} + code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; From 82ec56a97ab1f5f5b8dd622cc7d80c3e4806cbe2 Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Sat, 28 Jun 2025 15:29:56 +0300 Subject: [PATCH 4/4] fix annotation --- .../AnnotationList/AnnotationList.js | 4 ++-- .../AnnotationMain/AnnotationMain.js | 23 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/components/AnnotationList/AnnotationList.js b/src/components/AnnotationList/AnnotationList.js index 0430627..29efa7b 100644 --- a/src/components/AnnotationList/AnnotationList.js +++ b/src/components/AnnotationList/AnnotationList.js @@ -6,8 +6,8 @@ function AnnotationList({ annotations, onAnnotationClick }) {

          Annotations

            {annotations.map((annotation, index) => ( -
          • onAnnotationClick(annotation.time)}> - Frame {index + 1} - {annotation.annotations.length} objects +
          • onAnnotationClick(index)}> + Frame {index + 1} - {annotation.detections.length} objects
          • ))}
          diff --git a/src/components/AnnotationMain/AnnotationMain.js b/src/components/AnnotationMain/AnnotationMain.js index df9b849..66085b2 100644 --- a/src/components/AnnotationMain/AnnotationMain.js +++ b/src/components/AnnotationMain/AnnotationMain.js @@ -13,7 +13,7 @@ import { detectionTypes } from '../../constants/detectionTypes'; function AnnotationMain() { const [files, setFiles] = useState([]); const [selectedFile, setSelectedFile] = useState(null); - const [annotations, setAnnotations] = useState({}); + const [annotations, setAnnotations] = useState([]); const [currentTime, setCurrentTime] = useState(0); const [selectedClass, setSelectedClass] = useState(null); const [detections, setDetections] = useState([]); @@ -36,7 +36,7 @@ function AnnotationMain() { if (!file) return; setSelectedFile(file); - setAnnotations({}); + setAnnotations([]); setDetections([]); setSelectedDetectionIndices([]); setCurrentTime(0); @@ -79,11 +79,12 @@ function AnnotationMain() { if (imageData) { const newAnnotations = { - ...annotations, - [currentTime]: { time: currentTime, annotations: detections, imageData } + time: currentTime, + detections: detections, + imageData: imageData }; - setAnnotations(newAnnotations); + setAnnotations(prevAnnotation => [...prevAnnotation, newAnnotations]); saveAnnotation(currentTime, detections, imageData); setErrorMessage(""); @@ -106,15 +107,15 @@ function AnnotationMain() { setDetections([]); } - const handleAnnotationClick = (time) => { - setCurrentTime(time); - const annotation = annotations[time]; + const handleAnnotationClick = (index) => { + const annotation = annotations[index]; if (annotation) { - setDetections(annotation.annotations || []); + setCurrentTime(annotation.time); + setDetections(annotation.detections || []); setSelectedDetectionIndices([]); } if (videoRef.current) { - videoRef.current.currentTime = time; + videoRef.current.currentTime = annotation.time; } setIsPlaying(false); }; @@ -244,7 +245,7 @@ function AnnotationMain() {