From a3604f6d3639897fcce4f722185441bbc922462c Mon Sep 17 00:00:00 2001 From: Armen Rohalov Date: Tue, 6 May 2025 20:03:09 +0300 Subject: [PATCH] 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); + } }); }