PointTrackApp / src /components /EmptyView.vue
2nzi's picture
first commit
b4f9490 verified
<template>
<div class="export-view">
<div class="export-content">
<div class="export-header">
<h3>Export</h3>
</div>
<div class="export-info">
<div class="info-row">
<span>{{ objectCount }} objets, {{ annotationCount }} annotations</span>
</div>
<div class="info-row filename">
<span>{{ fileName }}</span>
</div>
</div>
<div class="export-actions">
<button
class="export-button"
@click="exportAnnotations"
:disabled="!hasAnnotations"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/>
</svg>
Télécharger
</button>
<p v-if="!hasAnnotations" class="no-data-message">
Aucune donnée à exporter
</p>
</div>
</div>
</div>
</template>
<script>
import { useAnnotationStore } from '@/stores/annotationStore'
import { useVideoStore } from '@/stores/videoStore'
import { computed } from 'vue'
export default {
name: 'EmptyView',
setup() {
const annotationStore = useAnnotationStore()
const videoStore = useVideoStore()
const videoName = computed(() => {
if (videoStore.selectedVideo?.name) {
return videoStore.selectedVideo.name.replace(/\.[^/.]+$/, '') // Enlever l'extension
}
return 'default_video'
})
const fileName = computed(() => {
return `${videoName.value}_config.json`
})
const objectCount = computed(() => {
return Object.keys(annotationStore.objects).length
})
const annotationCount = computed(() => {
let count = 0
Object.values(annotationStore.frameAnnotations).forEach(frameAnnotations => {
count += frameAnnotations.length
})
return count
})
const hasAnnotations = computed(() => {
return annotationCount.value > 0
})
const exportAnnotations = () => {
// Créer la structure objects selon le format demandé
const objects = Object.values(annotationStore.objects).map(obj => {
const objData = {
obj_id: parseInt(obj.id)
}
// Analyser le label pour extraire le type et l'équipe
if (obj.label) {
const label = obj.label.toLowerCase()
if (label.includes('ball')) {
objData.obj_type = 'ball'
objData.team = null
} else if (label.includes('player')) {
objData.obj_type = 'player'
// Extraire le numéro d'équipe
if (label.includes('team 1') || label.includes('team1')) {
objData.team = 1
} else if (label.includes('team 2') || label.includes('team2')) {
objData.team = 2
} else {
objData.team = null
}
} else {
objData.obj_type = null
objData.team = null
}
} else {
objData.obj_type = null
objData.team = null
}
return objData
})
// Créer la structure initial_annotations selon le format demandé
const initial_annotations = []
// Parcourir toutes les frames qui ont des annotations
Object.keys(annotationStore.frameAnnotations).forEach(frameNumber => {
const frameAnnotations = annotationStore.frameAnnotations[frameNumber]
const frameData = {
frame: parseInt(frameNumber),
annotations: []
}
// Grouper les annotations par objet pour cette frame
const annotationsByObject = {}
frameAnnotations.forEach(annotation => {
if (!annotationsByObject[annotation.objectId]) {
annotationsByObject[annotation.objectId] = []
}
// Extraire les points selon le type d'annotation
if (annotation.type === 'point') {
annotationsByObject[annotation.objectId].push({
x: Math.round(annotation.x),
y: Math.round(annotation.y),
label: annotation.pointType === 'positive' ? 1 : 0
})
} else if (annotation.type === 'mask' && annotation.points) {
// Ajouter tous les points du masque
annotation.points.forEach(point => {
annotationsByObject[annotation.objectId].push({
x: Math.round(point.x),
y: Math.round(point.y),
label: point.type === 'positive' ? 1 : 0
})
})
}
})
// Créer les annotations pour cette frame
Object.keys(annotationsByObject).forEach(objectId => {
const points = annotationsByObject[objectId]
if (points.length > 0) {
frameData.annotations.push({
obj_id: parseInt(objectId),
points: points
})
}
})
// Ajouter la frame seulement si elle a des annotations
if (frameData.annotations.length > 0) {
initial_annotations.push(frameData)
}
})
// Structure finale selon le format demandé
const exportData = {
objects: objects,
initial_annotations: initial_annotations
}
// Créer le blob et le télécharger
const jsonString = JSON.stringify(exportData, null, 2)
const blob = new Blob([jsonString], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = fileName.value
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
console.log('Annotations exportées:', fileName.value)
}
return {
videoName,
fileName,
objectCount,
annotationCount,
hasAnnotations,
exportAnnotations
}
}
}
</script>
<style scoped>
.export-view {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: white;
padding: 20px;
}
.export-content {
text-align: center;
width: 100%;
}
.export-header {
margin-bottom: 20px;
}
.export-header h3 {
margin: 0;
font-size: 1rem;
color: #fff;
font-weight: 500;
}
.export-info {
margin-bottom: 20px;
}
.info-row {
margin-bottom: 8px;
color: #ccc;
font-size: 0.9rem;
}
.info-row.filename {
color: #fff;
font-family: monospace;
font-size: 0.8rem;
}
.export-actions {
text-align: center;
}
.export-button {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: #4a4a4a;
color: white;
border: none;
border-radius: 4px;
font-size: 0.8rem;
cursor: pointer;
transition: background 0.2s;
}
.export-button:hover:not(:disabled) {
background: #5a5a5a;
}
.export-button:disabled {
background: #3c3c3c;
color: #666;
cursor: not-allowed;
}
.no-data-message {
margin: 12px 0 0 0;
color: #666;
font-size: 0.8rem;
font-style: italic;
}
</style>