mirror of
https://github.com/azaion/ui.git
synced 2026-04-23 03:36:35 +00:00
add type to detections
This commit is contained in:
@@ -8,6 +8,7 @@ import * as AnnotationService from '../../services/AnnotationService';
|
|||||||
import AnnotationControls from '../AnnotationControls/AnnotationControls';
|
import AnnotationControls from '../AnnotationControls/AnnotationControls';
|
||||||
import saveAnnotation from '../../services/DataHandler';
|
import saveAnnotation from '../../services/DataHandler';
|
||||||
import './AnnotationMain.css';
|
import './AnnotationMain.css';
|
||||||
|
import { detectionTypes } from '../../constants/detectionTypes';
|
||||||
|
|
||||||
function AnnotationMain() {
|
function AnnotationMain() {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
@@ -21,6 +22,7 @@ function AnnotationMain() {
|
|||||||
const [videoWidth, setVideoWidth] = useState(640);
|
const [videoWidth, setVideoWidth] = useState(640);
|
||||||
const [videoHeight, setVideoHeight] = useState(480);
|
const [videoHeight, setVideoHeight] = useState(480);
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
|
const [detectionType, setDetectionType] = useState(detectionTypes.day)
|
||||||
|
|
||||||
const videoRef = useRef(null);
|
const videoRef = useRef(null);
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
@@ -71,7 +73,8 @@ function AnnotationMain() {
|
|||||||
const imageData = AnnotationService.createAnnotationImage(
|
const imageData = AnnotationService.createAnnotationImage(
|
||||||
videoRef,
|
videoRef,
|
||||||
detections,
|
detections,
|
||||||
safeContainerRef
|
safeContainerRef,
|
||||||
|
detectionType
|
||||||
);
|
);
|
||||||
|
|
||||||
if (imageData) {
|
if (imageData) {
|
||||||
@@ -186,7 +189,11 @@ function AnnotationMain() {
|
|||||||
onDropNewFiles={handleDropNewFiles}
|
onDropNewFiles={handleDropNewFiles}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DetectionClassList onClassSelect={handleClassSelect} />
|
<DetectionClassList
|
||||||
|
onClassSelect={handleClassSelect}
|
||||||
|
detectionType={detectionType}
|
||||||
|
setDetectionType={setDetectionType}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='player-wrapper' >
|
<div className='player-wrapper' >
|
||||||
@@ -214,6 +221,7 @@ function AnnotationMain() {
|
|||||||
onDetectionsChange={handleDetectionsChange}
|
onDetectionsChange={handleDetectionsChange}
|
||||||
onSelectionChange={handleSelectionChange}
|
onSelectionChange={handleSelectionChange}
|
||||||
detectionClass={selectedClass}
|
detectionClass={selectedClass}
|
||||||
|
detectionType={detectionType}
|
||||||
/>
|
/>
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ function CanvasEditor({
|
|||||||
onDetectionsChange,
|
onDetectionsChange,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
children,
|
children,
|
||||||
detectionClass
|
detectionClass,
|
||||||
|
detectionType
|
||||||
}) {
|
}) {
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
const [currentDetection, setCurrentDetection] = useState(initialCurrentDetection);
|
const [currentDetection, setCurrentDetection] = useState(initialCurrentDetection);
|
||||||
@@ -258,6 +259,7 @@ function CanvasEditor({
|
|||||||
onDetectionMouseDown={handleDetectionMouseDown}
|
onDetectionMouseDown={handleDetectionMouseDown}
|
||||||
currentDetection={currentDetection}
|
currentDetection={currentDetection}
|
||||||
onResize={handleResize}
|
onResize={handleResize}
|
||||||
|
detectionType={detectionType}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
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) {
|
if (!detection || !detection.class) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -13,10 +14,6 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use startsWith to correctly handle RGBA and hex colors
|
// 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')
|
const borderColor = color.startsWith('rgba')
|
||||||
? color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 1)')
|
? color.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 1)')
|
||||||
: color;
|
: color;
|
||||||
@@ -40,7 +37,6 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) {
|
|||||||
top: `${detection.y1}px`,
|
top: `${detection.y1}px`,
|
||||||
width: `${detection.x2 - detection.x1}px`,
|
width: `${detection.x2 - detection.x1}px`,
|
||||||
height: `${detection.y2 - detection.y1}px`,
|
height: `${detection.y2 - detection.y1}px`,
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
border: `2px solid ${borderColor}`,
|
border: `2px solid ${borderColor}`,
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
cursor: isSelected ? 'move' : 'default',
|
cursor: isSelected ? 'move' : 'default',
|
||||||
@@ -50,7 +46,7 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) {
|
|||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
style.border = `3px solid black`;
|
style.border = `3px solid black`;
|
||||||
style.boxShadow = `0 0 4px 2px ${borderColor}`;
|
style.boxShadow = `0 0 4px 4px ${borderColor}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseDown = (e) => {
|
const handleMouseDown = (e) => {
|
||||||
@@ -92,7 +88,7 @@ function Detection({ detection, isSelected, onDetectionMouseDown, onResize }) {
|
|||||||
textShadow: '1px 1px 2px black',
|
textShadow: '1px 1px 2px black',
|
||||||
pointerEvents: 'none'
|
pointerEvents: 'none'
|
||||||
}}>
|
}}>
|
||||||
{detection.class.Name}
|
{detection.class.Name} {detectionType !== detectionTypes.day && '(' + detectionType + ')'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,4 +21,36 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
border-radius: 4px;
|
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;
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import DetectionClass from '../../models/DetectionClass';
|
import DetectionClass from '../../models/DetectionClass';
|
||||||
import './DetectionClassList.css';
|
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 [detectionClasses, setDetectionClasses] = useState([]);
|
||||||
const [selectedClass, setSelectedClass] = useState(null);
|
const [selectedClass, setSelectedClass] = useState(null);
|
||||||
|
|
||||||
@@ -83,6 +86,10 @@ function DetectionClassList({ onClassSelect }) {
|
|||||||
onClassSelect && onClassSelect(cls);
|
onClassSelect && onClassSelect(cls);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTypeClick = (type) => {
|
||||||
|
setDetectionType(type);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='class-list'>
|
<div className='class-list'>
|
||||||
<h3 className='menu-title'>Classes</h3>
|
<h3 className='menu-title'>Classes</h3>
|
||||||
@@ -107,6 +114,29 @@ function DetectionClassList({ onClassSelect }) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<div className='detection-type-group'>
|
||||||
|
<button className={detectionType == detectionTypes.day
|
||||||
|
? 'detection-type-btn active-type'
|
||||||
|
: 'detection-type-btn'} title='День'
|
||||||
|
onClick={() => handleTypeClick(detectionTypes.day)}>
|
||||||
|
<MdOutlineWbSunny />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className={detectionType == detectionTypes.night
|
||||||
|
? 'detection-type-btn active-type'
|
||||||
|
: 'detection-type-btn'} title='Ніч'
|
||||||
|
onClick={() => handleTypeClick(detectionTypes.night)}>
|
||||||
|
<MdOutlineNightlightRound />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className={detectionType == detectionTypes.winter
|
||||||
|
? 'detection-type-btn active-type'
|
||||||
|
: 'detection-type-btn'} title='Зима'
|
||||||
|
onClick={() => handleTypeClick(detectionTypes.winter)}>
|
||||||
|
<FaRegSnowflake />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Detection from './Detection';
|
import Detection from './Detection';
|
||||||
|
|
||||||
function DetectionContainer({ detections, selectedDetectionIndices, onDetectionMouseDown, currentDetection, onResize }) {
|
function DetectionContainer({ detections, selectedDetectionIndices, onDetectionMouseDown, currentDetection, onResize, detectionType }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -13,6 +13,7 @@ function DetectionContainer({ detections, selectedDetectionIndices, onDetectionM
|
|||||||
isSelected={selectedDetectionIndices.includes(index)}
|
isSelected={selectedDetectionIndices.includes(index)}
|
||||||
onDetectionMouseDown={(e) => onDetectionMouseDown(e, index)}
|
onDetectionMouseDown={(e) => onDetectionMouseDown(e, index)}
|
||||||
onResize={(e, position) => onResize(e, index, position)}
|
onResize={(e, position) => onResize(e, index, position)}
|
||||||
|
detectionType={detectionType}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{currentDetection && (
|
{currentDetection && (
|
||||||
@@ -21,6 +22,7 @@ function DetectionContainer({ detections, selectedDetectionIndices, onDetectionM
|
|||||||
isSelected={false}
|
isSelected={false}
|
||||||
onDetectionMouseDown={() => {}} // No-op handler for the current detection
|
onDetectionMouseDown={() => {}} // No-op handler for the current detection
|
||||||
onResize={() => {}} // No-op handler for the current detection
|
onResize={() => {}} // No-op handler for the current detection
|
||||||
|
detectionType={detectionType}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export const detectionTypes = {
|
||||||
|
day: 'day',
|
||||||
|
night: 'night',
|
||||||
|
winter: 'winter'
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { detectionTypes } from "../constants/detectionTypes";
|
||||||
|
|
||||||
export const calculateRelativeCoordinates = (e, containerRef) => {
|
export const calculateRelativeCoordinates = (e, containerRef) => {
|
||||||
if (!containerRef.current) return { x: 0, y: 0 };
|
if (!containerRef.current) return { x: 0, y: 0 };
|
||||||
const containerRect = containerRef.current.getBoundingClientRect();
|
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;
|
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) {
|
if (!videoRef?.current || !containerRef?.current) {
|
||||||
console.warn("Missing video or container reference");
|
console.warn("Missing video or container reference");
|
||||||
return null;
|
return null;
|
||||||
@@ -39,17 +41,12 @@ export const createAnnotationImage = (videoRef, detections, containerRef) => {
|
|||||||
detections.forEach(detection => {
|
detections.forEach(detection => {
|
||||||
if (!detection?.class) return;
|
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
|
// Ensure full opacity for border
|
||||||
const borderColor = detection.class.Color?.startsWith('rgba')
|
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.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/, 'rgba($1, $2, $3, 1)')
|
||||||
: detection.class.Color || 'rgba(255, 0, 0, 1)';
|
: detection.class.Color || 'rgba(255, 0, 0, 1)';
|
||||||
|
|
||||||
ctx.fillStyle = bgColor;
|
ctx.fillStyle = 'rgba(255, 255, 255, 0)';
|
||||||
ctx.strokeStyle = borderColor;
|
ctx.strokeStyle = borderColor;
|
||||||
|
|
||||||
const x = Math.max(0, detection.x1 || 0) * detection.kw;
|
const x = Math.max(0, detection.x1 || 0) * detection.kw;
|
||||||
@@ -63,7 +60,11 @@ export const createAnnotationImage = (videoRef, detections, containerRef) => {
|
|||||||
|
|
||||||
ctx.fillStyle = 'white';
|
ctx.fillStyle = 'white';
|
||||||
ctx.font = '12px Arial';
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user