Spaces:
Running
Running
<template> | |
<div class="annotations-raw-view"> | |
<div class="raw-header"> | |
<h4>Annotations Brutes</h4> | |
<div class="frame-info"> | |
<span>Frame {{ currentFrameNumber }}</span> | |
<span v-if="selectedObject">{{ selectedObject.name }}</span> | |
</div> | |
</div> | |
<div class="raw-content" v-if="selectedAnnotations.length > 0"> | |
<div | |
v-for="annotation in selectedAnnotations" | |
:key="annotation.id" | |
class="annotation-item" | |
:class="`annotation-${annotation.type}`" | |
> | |
<div class="annotation-header"> | |
<span class="annotation-type">{{ getAnnotationTypeLabel(annotation.type) }}</span> | |
<span class="annotation-id">ID: {{ annotation.id.slice(0, 8) }}...</span> | |
</div> | |
<div class="annotation-data"> | |
<div v-if="annotation.type === 'rectangle'" class="rectangle-data"> | |
<div class="data-row"> | |
<span class="data-label">Position:</span> | |
<span class="data-value">{{ Math.round(annotation.x) }}, {{ Math.round(annotation.y) }}</span> | |
</div> | |
<div class="data-row"> | |
<span class="data-label">Dimensions:</span> | |
<span class="data-value">{{ Math.round(annotation.width) }} × {{ Math.round(annotation.height) }}</span> | |
</div> | |
</div> | |
<div v-else-if="annotation.type === 'point'" class="point-data"> | |
<div class="data-row"> | |
<span class="data-label">Position:</span> | |
<span class="data-value">{{ Math.round(annotation.x) }}, {{ Math.round(annotation.y) }}</span> | |
</div> | |
<div class="data-row"> | |
<span class="data-label">Type:</span> | |
<span class="data-value" :class="`point-${annotation.pointType}`"> | |
{{ annotation.pointType === 'positive' ? 'Positif (+)' : 'Négatif (-)' }} | |
</span> | |
</div> | |
</div> | |
<div v-else-if="annotation.type === 'mask'" class="mask-data"> | |
<div class="data-row"> | |
<span class="data-label">Score:</span> | |
<span class="data-value">{{ (annotation.maskScore * 100).toFixed(1) }}%</span> | |
</div> | |
<div class="data-row"> | |
<span class="data-label">Taille image:</span> | |
<span class="data-value">{{ annotation.maskImageSize?.width }} × {{ annotation.maskImageSize?.height }}</span> | |
</div> | |
<div class="data-row"> | |
<span class="data-label">Points:</span> | |
<span class="data-value">{{ annotation.points?.length || 0 }} points</span> | |
</div> | |
<div v-if="annotation.points && annotation.points.length > 0" class="points-details"> | |
<div v-for="(point, pointIndex) in annotation.points" :key="pointIndex" class="point-detail"> | |
<span class="point-coords">{{ Math.round(point.x) }}, {{ Math.round(point.y) }}</span> | |
<span class="point-type" :class="`point-${point.type}`">{{ point.type }}</span> | |
</div> | |
</div> | |
</div> | |
<div v-else class="generic-data"> | |
<div class="data-row"> | |
<span class="data-label">Données:</span> | |
<span class="data-value">{{ JSON.stringify(annotation, null, 2) }}</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div v-else class="no-annotations"> | |
<div class="no-annotations-content"> | |
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor"> | |
<path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,16.5L6.5,12L7.91,10.59L11,13.67L16.59,8.09L18,9.5L11,16.5Z" opacity="0.3"/> | |
</svg> | |
<p v-if="!selectedObject">Aucun objet sélectionné</p> | |
<p v-else>Aucune annotation pour cet objet sur cette frame</p> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import { useAnnotationStore } from '@/stores/annotationStore' | |
import { useVideoStore } from '@/stores/videoStore' | |
import { computed } from 'vue' | |
export default { | |
name: 'AnnotationsRawView', | |
setup() { | |
const annotationStore = useAnnotationStore() | |
const videoStore = useVideoStore() | |
const getCurrentFrameNumber = () => { | |
const frameRate = annotationStore.currentSession?.frameRate || 30 | |
return Math.round(videoStore.currentTime * frameRate) | |
} | |
const currentFrameNumber = computed(() => getCurrentFrameNumber()) | |
const selectedObject = computed(() => { | |
if (!annotationStore.selectedObjectId) return null | |
return annotationStore.objects[annotationStore.selectedObjectId] | |
}) | |
const selectedAnnotations = computed(() => { | |
const currentFrame = getCurrentFrameNumber() | |
const frameAnnotations = annotationStore.getAnnotationsForFrame(currentFrame) || [] | |
return frameAnnotations.filter( | |
annotation => annotation && annotation.objectId === annotationStore.selectedObjectId | |
) | |
}) | |
return { | |
currentFrameNumber, | |
selectedObject, | |
selectedAnnotations | |
} | |
}, | |
methods: { | |
getAnnotationTypeLabel(type) { | |
const labels = { | |
'rectangle': 'Rectangle', | |
'point': 'Point', | |
'mask': 'Masque', | |
'polygon': 'Polygone' | |
} | |
return labels[type] || type.charAt(0).toUpperCase() + type.slice(1) | |
} | |
} | |
} | |
</script> | |
<style scoped> | |
.annotations-raw-view { | |
height: 100%; | |
display: flex; | |
flex-direction: column; | |
color: white; | |
} | |
.raw-header { | |
padding: 12px; | |
border-bottom: 1px solid #4a4a4a; | |
} | |
.raw-header h4 { | |
margin: 0 0 4px 0; | |
font-size: 0.9rem; | |
color: #fff; | |
} | |
.frame-info { | |
display: flex; | |
gap: 12px; | |
font-size: 0.8rem; | |
color: #ccc; | |
} | |
.raw-content { | |
flex: 1; | |
padding: 8px; | |
overflow-y: auto; | |
display: flex; | |
flex-direction: column; | |
gap: 8px; | |
} | |
.annotation-item { | |
background: #3c3c3c; | |
border-radius: 6px; | |
padding: 8px; | |
border-left: 3px solid #4ecdc4; | |
} | |
.annotation-item.annotation-rectangle { | |
border-left-color: #00ff00; | |
} | |
.annotation-item.annotation-point { | |
border-left-color: #ff6b35; | |
} | |
.annotation-item.annotation-mask { | |
border-left-color: #a55eea; | |
} | |
.annotation-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 6px; | |
} | |
.annotation-type { | |
font-weight: 500; | |
font-size: 0.8rem; | |
color: #fff; | |
} | |
.annotation-id { | |
font-size: 0.7rem; | |
color: #999; | |
font-family: monospace; | |
} | |
.annotation-data { | |
font-size: 0.8rem; | |
} | |
.data-row { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 4px; | |
} | |
.data-label { | |
color: #ccc; | |
font-weight: 500; | |
} | |
.data-value { | |
color: #fff; | |
font-family: monospace; | |
} | |
.point-positive { | |
color: #00ff00; | |
} | |
.point-negative { | |
color: #ff0000; | |
} | |
.points-details { | |
margin-top: 6px; | |
padding-left: 8px; | |
border-left: 1px solid #555; | |
} | |
.point-detail { | |
display: flex; | |
justify-content: space-between; | |
font-size: 0.7rem; | |
margin-bottom: 2px; | |
} | |
.point-coords { | |
color: #ccc; | |
font-family: monospace; | |
} | |
.point-type { | |
font-weight: 500; | |
} | |
.no-annotations { | |
flex: 1; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.no-annotations-content { | |
text-align: center; | |
color: #666; | |
} | |
.no-annotations-content svg { | |
margin-bottom: 16px; | |
} | |
.no-annotations-content p { | |
margin: 0; | |
font-style: italic; | |
} | |
</style> |