Spaces:
Running
Running
| // Dans un store Pinia (stores/annotationStore.js) | |
| import { defineStore } from 'pinia' | |
| import { v4 as uuidv4 } from 'uuid' | |
| export const useAnnotationStore = defineStore('annotations', { | |
| state: () => ({ | |
| // Session courante avec métadonnées vidéo | |
| currentSession: { | |
| id: 'session-1', | |
| name: 'Session d\'annotation', | |
| videoId: null, | |
| video: null, // Nom du fichier vidéo | |
| metadata: { | |
| duration: 0, | |
| width: 0, | |
| height: 0, | |
| fps: 30, | |
| date: new Date().toISOString() | |
| } | |
| }, | |
| // Dictionnaire des objets | |
| objects: { | |
| // Format: "1": { id: "1", name: "Objet 1", color: "#4f056f" } | |
| }, | |
| // Compteur pour les IDs d'objets | |
| objectIdCounter: 1, | |
| // Annotations par frame | |
| frameAnnotations: {}, // Format: { | |
| // "0": [ | |
| // { | |
| // id: "uuid", | |
| // objectId: "1", | |
| // type: "mask", | |
| // mask: "base64...", | |
| // maskScore: 0.91, | |
| // maskImageSize: { width: 1920, height: 1080 }, | |
| // points: [{ x: 1270, y: 405, type: "positive" }] | |
| // }, | |
| // { | |
| // id: "uuid", | |
| // objectId: "2", | |
| // type: "rectangle", | |
| // x: 100, | |
| // y: 100, | |
| // width: 200, | |
| // height: 150 | |
| // } | |
| // ] | |
| // } | |
| selectedObjectId: null, | |
| temporaryPoints: [] | |
| }), | |
| getters: { | |
| getTemporaryPointsForObject: (state) => (objectId) => { | |
| return state.temporaryPoints.filter(point => point.objectId === objectId) | |
| } | |
| }, | |
| actions: { | |
| // Sélectionner un objet | |
| selectObject(objectId) { | |
| this.selectedObjectId = objectId | |
| console.log(`Objet sélectionné: ${objectId}`) | |
| }, | |
| // Désélectionner l'objet actuel | |
| deselectObject() { | |
| this.selectedObjectId = null | |
| }, | |
| // Mettre à jour les métadonnées de la vidéo | |
| updateVideoMetadata(metadata) { | |
| this.currentSession.metadata = { | |
| ...this.currentSession.metadata, | |
| ...metadata | |
| } | |
| }, | |
| // Ajouter une annotation pour l'objet sélectionné à la frame actuelle | |
| addAnnotation(frameNumber, annotation) { | |
| const id = uuidv4() | |
| let newAnnotation | |
| if (annotation.type === 'mask') { | |
| newAnnotation = { | |
| id, | |
| objectId: this.selectedObjectId, | |
| type: 'mask', | |
| mask: annotation.mask || '', | |
| maskScore: annotation.maskScore || 0, | |
| maskImageSize: annotation.maskImageSize || { | |
| width: this.currentSession.metadata.width, | |
| height: this.currentSession.metadata.height | |
| }, | |
| points: annotation.points || [] | |
| } | |
| } else if (annotation.type === 'rectangle') { | |
| newAnnotation = { | |
| id, | |
| objectId: this.selectedObjectId, | |
| type: 'rectangle', | |
| x: annotation.x, | |
| y: annotation.y, | |
| width: annotation.width, | |
| height: annotation.height | |
| } | |
| } else if (annotation.type === 'point') { | |
| newAnnotation = { | |
| id, | |
| objectId: this.selectedObjectId, | |
| type: 'point', | |
| x: annotation.x, | |
| y: annotation.y, | |
| pointType: annotation.pointType // 'positive' ou 'negative' | |
| } | |
| } else { | |
| // Gestion par défaut pour les autres types | |
| newAnnotation = { | |
| id, | |
| objectId: this.selectedObjectId, | |
| ...annotation | |
| } | |
| } | |
| // Vérifier que newAnnotation a été créée | |
| if (!newAnnotation) { | |
| console.error('Erreur: impossible de créer l\'annotation', annotation) | |
| return null | |
| } | |
| if (!this.frameAnnotations[frameNumber]) { | |
| this.frameAnnotations[frameNumber] = [] | |
| } | |
| this.frameAnnotations[frameNumber].push(newAnnotation) | |
| console.log('Annotation ajoutée:', newAnnotation) | |
| return id | |
| }, | |
| updateAnnotation(frameNumber, annotationId, updates) { | |
| if (!this.frameAnnotations[frameNumber]) return | |
| const annotationIndex = this.frameAnnotations[frameNumber].findIndex( | |
| a => a.id === annotationId | |
| ) | |
| if (annotationIndex === -1) return | |
| // Mettre à jour l'annotation avec les nouvelles propriétés | |
| this.frameAnnotations[frameNumber][annotationIndex] = { | |
| ...this.frameAnnotations[frameNumber][annotationIndex], | |
| ...updates | |
| } | |
| }, | |
| // Ajouter un nouvel objet | |
| addObject(objectData = {}) { | |
| const objectId = `${this.objectIdCounter++}` | |
| this.objects[objectId] = { | |
| id: objectId, | |
| name: objectData.name || `Objet ${this.objectIdCounter - 1}`, | |
| color: objectData.color || this.getRandomColor(), | |
| // Autres propriétés selon vos besoins | |
| } | |
| // Sélectionner automatiquement le nouvel objet | |
| this.selectObject(objectId) | |
| return objectId | |
| }, | |
| // Récupérer les annotations pour une frame | |
| getAnnotationsForFrame(frameNumber) { | |
| return this.frameAnnotations[frameNumber.toString()] || [] | |
| }, | |
| // Supprimer une annotation | |
| removeAnnotation(frameNumber, annotationId) { | |
| const frameKey = frameNumber.toString() | |
| if (this.frameAnnotations[frameKey]) { | |
| this.frameAnnotations[frameKey] = this.frameAnnotations[frameKey] | |
| .filter(a => a.id !== annotationId) | |
| } | |
| }, | |
| // Nouvelle méthode pour supprimer tous les masques d'un objet sur une frame | |
| removeMasksForObject(frameNumber, objectId) { | |
| const frameKey = frameNumber.toString() | |
| if (this.frameAnnotations[frameKey]) { | |
| this.frameAnnotations[frameKey] = this.frameAnnotations[frameKey] | |
| .filter(a => !(a.objectId === objectId && a.type === 'mask')) | |
| } | |
| }, | |
| // Vérifier si un objet a encore des annotations sur une frame | |
| hasAnnotationsForObject(frameNumber, objectId) { | |
| const frameKey = frameNumber.toString() | |
| if (!this.frameAnnotations[frameKey]) return false | |
| return this.frameAnnotations[frameKey] | |
| .some(a => a.objectId === objectId) | |
| }, | |
| // Générer une couleur aléatoire | |
| getRandomColor() { | |
| return '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0') | |
| }, | |
| // Sauvegarder les annotations dans le format demandé | |
| saveAnnotations() { | |
| const data = { | |
| video: this.currentSession.video, | |
| metadata: this.currentSession.metadata, | |
| objects: this.objects, | |
| annotations: this.frameAnnotations | |
| } | |
| localStorage.setItem('annotations', JSON.stringify(data)) | |
| }, | |
| // Charger les annotations depuis le format demandé | |
| loadAnnotations() { | |
| const saved = localStorage.getItem('annotations') | |
| if (saved) { | |
| const data = JSON.parse(saved) | |
| this.currentSession.video = data.video | |
| this.currentSession.metadata = data.metadata | |
| this.objects = data.objects | |
| this.frameAnnotations = data.annotations | |
| } | |
| }, | |
| getAnnotation(frameNumber, annotationId) { | |
| if (!this.frameAnnotations[frameNumber]) return null | |
| return this.frameAnnotations[frameNumber].find(a => a.id === annotationId) || null | |
| }, | |
| addTemporaryPoint(point) { | |
| // Ajouter un ID unique au point | |
| const pointWithId = { | |
| ...point, | |
| id: uuidv4() | |
| } | |
| this.temporaryPoints.push(pointWithId) | |
| return pointWithId.id | |
| }, | |
| removeTemporaryPoint(pointId) { | |
| const index = this.temporaryPoints.findIndex(p => p.id === pointId) | |
| if (index !== -1) { | |
| this.temporaryPoints.splice(index, 1) | |
| } | |
| }, | |
| clearTemporaryPoints() { | |
| this.temporaryPoints = [] | |
| }, | |
| // Supprimer un objet et toutes ses annotations | |
| deleteObject(objectId) { | |
| // Supprimer l'objet du dictionnaire | |
| delete this.objects[objectId] | |
| // Supprimer toutes les annotations associées à cet objet | |
| Object.keys(this.frameAnnotations).forEach(frameKey => { | |
| this.frameAnnotations[frameKey] = this.frameAnnotations[frameKey] | |
| .filter(annotation => annotation.objectId !== objectId) | |
| }) | |
| // Si l'objet supprimé était sélectionné, désélectionner | |
| if (this.selectedObjectId === objectId) { | |
| this.selectedObjectId = null | |
| } | |
| // Supprimer les points temporaires associés à cet objet | |
| this.temporaryPoints = this.temporaryPoints.filter(point => point.objectId !== objectId) | |
| } | |
| } | |
| }) |