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.js b/src/components/AnnotationList.js deleted file mode 100644 index 241964f..0000000 --- a/src/components/AnnotationList.js +++ /dev/null @@ -1,20 +0,0 @@ -// src/components/AnnotationList.js -// No changes -import React from 'react'; - -function AnnotationList({ annotations, onAnnotationClick }) { - return ( -
-

Annotations

- -
- ); -} - -export default AnnotationList; \ No newline at end of file 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/AnnotationList.js b/src/components/AnnotationList/AnnotationList.js new file mode 100644 index 0000000..29efa7b --- /dev/null +++ b/src/components/AnnotationList/AnnotationList.js @@ -0,0 +1,18 @@ +import './AnnotationList.css' + +function AnnotationList({ annotations, onAnnotationClick }) { + return ( +
+

Annotations

+ +
+ ); +} + +export default AnnotationList; \ No newline at end of file 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 bbf61eb..436e1b0 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'; @@ -8,11 +8,12 @@ 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([]); 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([]); @@ -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); @@ -34,7 +36,7 @@ function AnnotationMain() { if (!file) return; setSelectedFile(file); - setAnnotations({}); + setAnnotations([]); setDetections([]); setSelectedDetectionIndices([]); setCurrentTime(0); @@ -71,16 +73,18 @@ function AnnotationMain() { const imageData = AnnotationService.createAnnotationImage( videoRef, detections, - safeContainerRef + safeContainerRef, + detectionType ); 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(""); @@ -103,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); }; @@ -190,7 +194,11 @@ function AnnotationMain() { onDropNewFiles={handleDropNewFiles} /> - +
@@ -218,6 +226,7 @@ function AnnotationMain() { onDetectionsChange={handleDetectionsChange} onSelectionChange={handleSelectionChange} detectionClass={selectedClass} + detectionType={detectionType} />
@@ -240,7 +249,7 @@ function AnnotationMain() {
diff --git a/src/components/CanvasEditor/CanvasEditor.js b/src/components/CanvasEditor/CanvasEditor.js index 5b8503d..d779f9b 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..0b642bb 100644 --- a/src/components/DetectionClassList/DetectionClassList.css +++ b/src/components/DetectionClassList/DetectionClassList.css @@ -1,24 +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: 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: #0e2060; +} + +.active-type { + color: white; + background: #3861FB; +} + +.active-type:hover { + cursor: default; + background: #3861FB; } \ No newline at end of file diff --git a/src/components/DetectionClassList/DetectionClassList.js b/src/components/DetectionClassList/DetectionClassList.js index a56dd54..a8d68a0 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,30 +86,60 @@ function DetectionClassList({ onClassSelect }) { onClassSelect && onClassSelect(cls); }; - return ( -
-

Classes

-
    - {detectionClasses.map((cls) => { - const backgroundColor = calculateColor(cls.Id); - const darkBg = calculateColor(cls.Id, '0.8'); - const isSelected = selectedClass && selectedClass.Id === cls.Id; + const handleTypeClick = (type) => { + setDetectionType(type); + } - return ( -
  • handleClassClick(cls)} - > - {cls.Name} -
  • - ); - })} -
+ 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} +
  • + ); + })} +
+
+ +
+ + + + + +
); } 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/components/MediaList/MediaList.css b/src/components/MediaList/MediaList.css index 9dccef3..bc70a8e 100644 --- a/src/components/MediaList/MediaList.css +++ b/src/components/MediaList/MediaList.css @@ -1,26 +1,73 @@ +.explorer{ + height: 40vh ; + background: #222531; + padding: 8px; + border-radius: 4px; + min-height: 180px; +} + +.explorer-head{ + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 6px; +} + .menu-title { - font-size: 14px; - margin: 15px 0; + font-size: 18px; + line-height: 20px; + color: white; + margin: 0; + margin-right: 10px; +} + +.open-btn{ + width: 80px; + height: 20px; + background: #6188FF; + color: white; + border: 0; + border-radius: 4px; + padding: 0; +} + +.open-btn:hover{ + background: #295cf7; } .file-filter{ box-sizing: border-box; width: 100%; - padding: 4px 8px; + height: 26px; + background: white; + padding: 6px 12px; + border: 0; + border-radius: 2px; + font-size: 14px; } .file-list-group { + display: flex; + flex-direction: column; + gap: 4px; padding: 0; + margin: 12px 0; list-style-type: none; - max-height: 20vh; overflow: auto; + scrollbar-width: none; + max-height: 36%; +} + +.file-list-group::-webkit-scrollbar { + display: none; } .file-list-item { - cursor: pointer; - padding: 6px; - border-bottom: 1px solid #eee; + padding: 7px 6px; font-size: 12px; + color: white; + cursor: pointer; + border-radius: 2px; } .label { @@ -28,9 +75,14 @@ } .file-input-block { + display: flex; + justify-content: center; + align-items: center; + height: 12%; + color: white; border: 2px dashed #ccc; + border-radius: 4px; padding: 8px; text-align: center; - margin-top: 10px; cursor: pointer; } \ No newline at end of file diff --git a/src/components/MediaList/MediaList.js b/src/components/MediaList/MediaList.js index 07e77cd..fc66f85 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())); @@ -21,15 +28,23 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) { return (
-

Files

- +
+

Files

+ + +
+
    {filteredFiles.map((file) => (
  • onFileSelect(file)} > @@ -39,6 +54,9 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
+
+ +
{isDragActive ? (

Drop here

) : ( 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/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; 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); + } }); }