Skip to content

Formats de transcription

Tous les formats de transcription et de sous-titres que Potato lit : Whisper, WhisperX, Deepgram, AssemblyAI, AWS Transcribe, SRT, WebVTT, TTML, sous-titres YouTube, CTM, Praat TextGrid et ELAN EAF, ainsi que les fichiers annexes et le modèle de tours normalisé.

Potato lit 21 entrées de transcription et de sous-titres et les convertit toutes vers le même modèle de tours avant tout affichage. Quel que soit ce qu'a produit votre chaîne de reconnaissance vocale ou votre outil de sous-titrage, vous pouvez y pointer Potato sans écrire de script de conversion. Cette page est la référence de ce qui fonctionne, de la façon dont chaque format est reconnu et de ce que chacun apporte. Pour le pas-à-pas, voir Comment annoter des transcriptions Whisper et Comment annoter des sous-titres YouTube.

Une transcription affichée en bulles de locuteur colorées, chacune avec son bouton de lecture, ses horodatages et une question d'étiquetage par tour.Un fichier SRT annexe affiché en bulles de locuteur, avec une question rattachée à chaque tour

Potato ne transcrit pas

La reconnaissance automatique de la parole et la diarisation ont lieu avant que Potato ne voie quoi que ce soit. Lancez d'abord Whisper, WhisperX, pyannote ou une API cloud ; Potato ingère le résultat. Rien sur cette page n'exécute de modèle.

Note: Le mode Think-Aloud exécute bien un modèle Whisper local, mais à une autre fin : il enregistre les annotateurs qui parlent pendant qu'ils travaillent. C'est une fonction de capture, pas d'ingestion de transcriptions.

La détection se fait sur le contenu, pas sur l'extension

Potato inspecte ce qu'il y a dans le fichier plutôt que de se fier à son nom. Un fichier WebVTT nommé captions.txt est toujours analysé comme du WebVTT. Conséquence pratique : la même transcription fonctionne aussi bien incorporée dans votre fichier de données que lue depuis un fichier annexe sur le disque, sans changement de configuration.

Formats pris en charge

Sorties de reconnaissance vocale

FormatDétecté parLocuteursTemps par mot
Whisper JSONTableau segmentsNonOui, avec --word_timestamps
WhisperX / JSON diarisésegments avec speakerOuiOui
whisper.cpp JSONTableau transcriptionNonNon
Whisper TSVEn-tête start/end/textNonNon
AWS Transcriberesults.items ou results.audio_segmentsOuiOui
Deepgramresults.channels ou results.utterancesOui, avec diarize=trueOui
AssemblyAItext plus words/utterancesOui, avec speaker_labelsOui
Rev.aiTableau monologuesOuiOui
SPoRCLignes turn_text/turnTextOui, inférésNon

Sous-titres

FormatDétecté parLocuteurs
SubRip (.srt)Flèches de bloc, séparateur ,mmmPar un préfixe Name:
WebVTT (.vtt)En-tête WEBVTTPar des balises <v Name> ou un préfixe Name:
SubStation Alpha (.ass, .ssa)[Script Info] / Dialogue:Par le champ Name
TTML / DFXPÉlément racine <tt>Par un attribut speaker/agent
YouTube srv1/srv2/srv3XML <transcript><text>Par un préfixe Name:
YouTube json3Tableau eventsAucun ; les sous-titres automatiques n'en portent pas

SubRip et WebVTT couvrent l'essentiel de ce que vous rencontrerez. TTML apparaît dans les archives de diffusion et dans les exports d'outils professionnels de sous-titrage.

Alignement et annotation linguistique

FormatDétecté parLocuteurs
NIST CTMColonnes séparées par des espaces, début et durée numériquesPar le champ de canal
Praat TextGridFile type = "ooTextFile"Une couche par locuteur
ELAN EAFRacine ANNOTATION_DOCUMENTPar PARTICIPANT, sinon l'identifiant de couche

Les sérialisations longue et courte de TextGrid sont toutes deux analysées. Pour les fichiers ELAN, Potato résout ALIGNABLE_ANNOTATION et REF_ANNOTATION contre la table TIME_ORDER et lit la référence au média dans l'en-tête.

Un Praat TextGrid affiché comme deux locuteurs nommés fieldworker et speaker, un par couche, les intervalles de silence étant omis.Chaque couche du TextGrid devient un locuteur, et les intervalles vides entre elles sont ignorés

Tout le reste

Trois formes génériques complètent les 21 :

  • Une simple liste de dictionnaires {speaker, start, end, text}.
  • Un objet {"audio": ..., "turns": [...]}.
  • Un paragraphe brut sans aucun horodatage, affiché comme une seule bulle.

Non pris en charge

Aucun analyseur n'existe pour ceux-ci. Convertissez-les d'abord.

  • SAMI (.smi), MicroDVD (.sub), SubViewer (.sbv)
  • Transcriber (.trs), EXMARaLDA, CHAT/CHILDES (.cha)
  • Les sorties natives de Montreal Forced Aligner et de Gentle. Exportez plutôt en CTM ou TextGrid depuis ces outils.
  • Azure Speech, Google Speech-to-Text et le verbose_json de l'API Whisper dans des enveloppes inhabituelles
  • Les fichiers de diarisation RTTM seuls

La confiance au niveau du mot est analysée et conservée dans le modèle de données quand la source la fournit, mais il n'existe pour l'instant aucune interface pour la consulter.

Le modèle de tours normalisé

Tous les formats ci-dessus deviennent ceci :

json
{
  "audio": "media/interview_01.mp3",
  "turns": [
    {
      "turn_id": "t0",
      "speaker": "host",
      "start": 0.0,
      "end": 6.5,
      "text": "Welcome back.",
      "words": [{"word": "Welcome", "start": 0.0, "end": 0.4, "confidence": 0.99}],
      "confidence": 0.97
    }
  ]
}

words et confidence n'apparaissent que si la source les portait. speaker vaut null pour les tours non diarisés, qui s'affichent comme Unassigned avec un sélecteur pour que les annotateurs les complètent.

Trois tours de transcription sur fond gris hachuré, chacun étiqueté Unassigned avec une flèche de menu déroulant, parce que le format source ne portait aucune étiquette de locuteur.Les sous-titres automatiques YouTube arrivent sans locuteurs, donc chaque tour reste Unassigned jusqu'à ce qu'un annotateur en choisisse un

turn_id est la clé de persistance des annotations par tour et des attributions de locuteur. Elle vient d'une chaîne turn_id ou step_id explicite dans la source quand il y en a une, sinon de t{index}. Le même fichier produit toujours les mêmes identifiants, donc les annotations survivent à un rechargement.

Warning: Les unités de temps diffèrent d'un outil à l'autre. Whisper et Deepgram utilisent des secondes en virgule flottante ; AssemblyAI, les décalages de whisper.cpp et la sortie TSV de Whisper utilisent des millisecondes entières. Potato convertit à la frontière pour que tout ce qui suit soit en secondes. Si votre propre prétraitement mélange les deux, les temps ressortent 1000 fois faux.

Fichiers annexes

Une valeur de champ qui est un chemin court d'une seule ligne se terminant par une extension de transcription connue est lue depuis le disque au lieu d'être traitée comme du texte. Ainsi, la disposition que votre outil de reconnaissance vocale a déjà produite, avec les fichiers médias à côté de leur .srt ou .json, fonctionne sans étape de prétraitement :

json
{
  "id": "int_001",
  "conversation": {
    "audio": "media/int_001.mp3",
    "transcript": "media/int_001.srt"
  }
}

Les chemins sont résolus relativement à task_dir et passent par la même validation de sécurité que tout autre chemin configuré, donc un fichier de données ne peut pas lire en dehors du projet.

Extensions reconnues : .srt .vtt .webvtt .json .json3 .srv1 .srv2 .srv3 .ttml .dfxp .xml .ass .ssa .tsv .ctm .TextGrid .eaf .txt

Si vos données contiennent réellement des transcriptions d'une ligne qui ressemblent à des noms de fichiers, désactivez l'heuristique :

yaml
display_options:
  transcript_is_path: auto   # auto (default) | true | false

Configuration

Affichage : audio_dialogue

L'affichage de dialogue audio présente les tours en bulles de locuteur synchronisées avec l'audio, avec un bouton de lecture sur chaque tour.

yaml
instance_display:
  fields:
    - key: conversation
      type: audio_dialogue
      label: "Transcript"
      span_target: true
      display_options:
        audio_key: audio
        turns_key: turns
        speaker_key: speaker
        text_key: text
        transcript_is_path: auto
        show_timestamps: true
        allow_speaker_assignment: auto
        scroll_height: 460px
OptionPar défautEffet
audio_keyaudioSous-clé de la valeur du champ contenant l'URL ou le chemin de l'audio.
turns_keyturnsSous-clé contenant la liste des tours. Accepte aussi segments.
speaker_key / text_keyspeaker / textClés par tour.
speakers[]Liste de {id, name, color, side}. Les locuteurs absents de la liste reçoivent une couleur déterministe et un côté alterné.
allow_speaker_assignmentautoauto active l'attribution au clic quand il y a des tours non diarisés ou une liste à corriger. true la force, false la désactive.
transcript_is_pathautoIndique s'il faut lire la valeur du champ comme un chemin vers un fichier annexe.
show_timestampstrueAffiche mm:ss–mm:ss sur chaque tour.
scroll_height480pxHauteur du panneau de transcription défilant.
playback_rates[1, 1.25, 1.5, 2]Options du sélecteur de vitesse.

Schémas

Depuis la 2.7.1, speech_transcript, voice_interaction et tiered_annotation lisent tous une transcription directement dans l'enregistrement de l'instance et acceptent tous les formats de cette page. Auparavant, seul l'affichage audio_dialogue le faisait.

yaml
annotation_schemes:
  - annotation_type: speech_transcript
    name: transcript_review
    description: "Mark transcription errors"
    segments_key: segments       # record field holding the transcript
 
  - annotation_type: voice_interaction
    name: barge_in
    description: "Mark overlaps and barge-in"
    turns_key: turns

L'annotation en couches peut préremplir une couche à partir de la transcription, de sorte que les annotateurs corrigent un alignement existant plutôt que de resegmenter la parole à la main :

yaml
  - annotation_type: tiered_annotation
    name: tiers
    source_field: audio_url
    media_type: audio
    transcript_field: asr_output   # opt-in; omit to start from a blank timeline
    transcript_tier: utterance     # defaults to the first tier
    tiers:
      - name: utterance
        labels:
          - name: speech
            color: "#7c3aed"

Les annotations préremplies ne sont pas écrites tant que l'annotateur n'a pas fait une vraie modification, donc rien n'est attribué à quelqu'un qui a seulement ouvert l'instance.

Construire un fichier de données avec potato transcripts

Pointez le convertisseur vers un dossier de sorties de reconnaissance vocale et il écrit un fichier de données prêt à annoter :

bash
# Pair transcripts to media by basename
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.json
 
# Media served from elsewhere
potato transcripts './captions/*.vtt' \
  --media-url-prefix https://cdn.example.org/audio -o data/talks.json
 
# What did it detect? Writes nothing.
potato transcripts ./whisper_out --dry-run

--dry-run rend compte de ce qu'il a trouvé sans rien écrire, ce qui est le moyen le plus rapide de repérer un dossier contenant le mauvais fichier de sortie Whisper :

text
Scanned 3 file(s):
  talk_01.srt          SRT                  2 turns      5.0s  2 speaker(s): Alice, Bob
  talk_02.vtt          WebVTT               1 turns      3.0s  undiarized
  talk_03.json         whisper.cpp JSON     1 turns      2.4s  undiarized

3 item(s), 4 turn(s).
1 item(s) have no media. Pass --media-dir or --media-url-prefix to enable playback.
OptionRôle
-o, --outputOù écrire. Obligatoire sauf avec --dry-run.
--formatjson (par défaut) ou jsonl.
--media-dirDossier de médias à apparier par nom de base.
--media-url-prefixURL de base des médias au lieu de fichiers locaux.
--fieldChamp de l'item où va la transcription. Par défaut conversation.
--id-prefixChaîne ajoutée devant chaque identifiant généré.
--speaker-keyClé source contenant l'étiquette de locuteur.
-r, --recursiveParcourt les sous-dossiers.
--dry-runIndique le format détecté et le nombre de tours par fichier.
--emit-configImprime aussi un fragment de config.yaml correspondant.
-q, --quietSupprime le rapport fichier par fichier.

Les identifiants d'item viennent du nom de fichier privé d'une extension de média finale, donc interview_01.mp3.json de Whisper devient interview_01 et non interview_01.mp3.

Réexporter

Les annotations en couches s'exportent vers ELAN EAF et Praat TextGrid, donc une transcription peut faire l'aller-retour : l'ingérer, l'annoter dans Potato, l'exporter, l'affiner dans ELAN ou Praat, puis relire le résultat.

bash
python -m potato.export --config config.yaml --format eaf --output ./out/
python -m potato.export --config config.yaml --format textgrid --output ./out/

Les deux exportateurs partent de la sortie de tiered_annotation. Les autres schémas s'exportent via les formats standard.

Dépannage

SymptômeCe qui s'est passé
Tout apparaît comme une seule grosse bulleLe format n'a pas été reconnu et est retombé sur le repli « paragraphe brut ». Lancez potato transcripts <file> --dry-run ; s'il annonce plain text, les temps n'ont jamais été analysés. Le fichier est généralement un .txt de Whisper, qui n'a aucun horodatage, plutôt que le .json ou le .srt.
Aucun locuteur, tout est UnassignedLa source ne porte aucune étiquette de locuteur. Whisper seul ne diarise pas, et les sous-titres automatiques YouTube non plus. Lancez la diarisation en amont ou laissez les annotateurs attribuer les locuteurs dans l'interface.
Les temps sont 1000 fois fauxSecondes et millisecondes ont été mélangées en amont. Les offsets de whisper.cpp, AssemblyAI et le TSV de Whisper sont tous en millisecondes.
Le chemin de la transcription s'affiche comme le texteLe fichier annexe n'a pas pu être lu, donc le chemin a été affiché comme contenu. Vérifiez qu'il se résout sous task_dir. Les journaux du serveur indiquent la raison précise.
Une transcription d'une ligne est lue comme un nom de fichierMettez transcript_is_path: false sur le champ d'affichage.

Projet d'exemple

examples/audio/transcript-formats/ dans le dépôt Potato affiche six formats côte à côte, chacun chargé depuis un fichier annexe : SubRip, WebVTT, Whisper JSON, YouTube json3, Praat TextGrid et Deepgram. Les six produisent les mêmes bulles de locuteur.

bash
python potato/flask_server.py start examples/audio/transcript-formats/config.yaml -p 8000

La même tâche existe en conception téléchargeable : Ingestion de formats de transcription.

Pour les détails d'implémentation, voir la documentation source.

Pour aller plus loin