Skip to content

Formatos de transcripción

Todos los formatos de transcripción y subtítulos que Potato lee: Whisper, WhisperX, Deepgram, AssemblyAI, AWS Transcribe, SRT, WebVTT, TTML, subtítulos de YouTube, CTM, Praat TextGrid y ELAN EAF, además de archivos adyacentes y el modelo de turnos normalizado.

Potato lee 21 entradas de transcripción y subtítulos, y convierte todas al mismo modelo de turnos antes de mostrar nada. Sea lo que sea lo que produjo tu flujo de ASR o tu herramienta de subtitulado, puedes apuntar Potato a ello sin escribir un script de conversión. Esta página es la referencia de qué funciona, cómo se reconoce cada formato y qué aporta cada uno. Para el recorrido paso a paso, consulta Cómo anotar transcripciones de Whisper y Cómo anotar subtítulos de YouTube.

Una transcripción mostrada como burbujas de hablante de colores, cada una con su botón de reproducción, marcas de tiempo y una pregunta de etiquetado por turno.Un archivo SRT adyacente mostrado como burbujas de hablante, con una pregunta adjunta a cada turno

Potato no transcribe

El reconocimiento de voz y la diarización de hablantes ocurren antes de que Potato vea nada. Ejecuta primero Whisper, WhisperX, pyannote o una API en la nube; Potato ingiere el resultado. Nada de esta página ejecuta un modelo.

Note: El modo Think-Aloud sí ejecuta un modelo Whisper local, pero con otro propósito: graba a los anotadores hablando mientras trabajan. Eso es una función de captura, no de ingesta de transcripciones.

La detección va por contenido, no por extensión

Potato inspecciona lo que hay dentro del archivo en lugar de fiarse de su nombre. Un archivo WebVTT llamado captions.txt se sigue analizando como WebVTT. La consecuencia práctica es que la misma transcripción funciona incrustada en tu archivo de datos o leída desde un archivo adyacente en disco, sin cambiar la configuración.

Formatos admitidos

Salida de ASR

FormatoSe detecta porHablantesTiempos por palabra
Whisper JSONArray segmentsNoSí, con --word_timestamps
WhisperX / JSON diarizadosegments con speaker
whisper.cpp JSONArray transcriptionNoNo
Whisper TSVCabecera start/end/textNoNo
AWS Transcriberesults.items o results.audio_segments
Deepgramresults.channels o results.utterancesSí, con diarize=true
AssemblyAItext más words/utterancesSí, con speaker_labels
Rev.aiArray monologues
SPoRCFilas turn_text/turnTextSí, inferidosNo

Subtítulos y rótulos

FormatoSe detecta porHablantes
SubRip (.srt)Flechas de bloque, separador ,mmmPor un prefijo Name:
WebVTT (.vtt)Cabecera WEBVTTPor etiquetas <v Name> o un prefijo Name:
SubStation Alpha (.ass, .ssa)[Script Info] / Dialogue:Por el campo Name
TTML / DFXPElemento raíz <tt>Por un atributo speaker/agent
YouTube srv1/srv2/srv3XML <transcript><text>Por un prefijo Name:
YouTube json3Array eventsNinguno; los subtítulos automáticos no traen hablantes

SubRip y WebVTT cubren casi todo lo que te encontrarás. TTML aparece en archivos de difusión y en exportaciones de herramientas profesionales de subtitulado.

Alineación y anotación lingüística

FormatoSe detecta porHablantes
NIST CTMColumnas separadas por espacios, inicio y duración numéricosPor el campo de canal
Praat TextGridFile type = "ooTextFile"Una capa por hablante
ELAN EAFRaíz ANNOTATION_DOCUMENTPor PARTICIPANT, si no, el id de la capa

Se analizan tanto la serialización larga como la corta de TextGrid. Para archivos de ELAN, Potato resuelve ALIGNABLE_ANNOTATION y REF_ANNOTATION contra la tabla TIME_ORDER y lee la referencia al material desde la cabecera.

Un Praat TextGrid mostrado como dos hablantes llamados fieldworker y speaker, uno por capa, con los intervalos de silencio omitidos.Cada capa del TextGrid se convierte en un hablante, y los intervalos vacíos entre ellas se omiten

Todo lo demás

Tres formas genéricas completan las 21:

  • Una lista simple de diccionarios {speaker, start, end, text}.
  • Un objeto {"audio": ..., "turns": [...]}.
  • Un párrafo llano sin tiempos, que se muestra como una única burbuja.

No admitidos

No existe analizador para estos. Conviértelos antes.

  • SAMI (.smi), MicroDVD (.sub), SubViewer (.sbv)
  • Transcriber (.trs), EXMARaLDA, CHAT/CHILDES (.cha)
  • Salida nativa de Montreal Forced Aligner y Gentle. Exporta desde ellos a CTM o TextGrid.
  • Azure Speech, Google Speech-to-Text y verbose_json de la API de Whisper dentro de envoltorios poco habituales
  • Archivos de diarización RTTM por sí solos

La confianza a nivel de palabra se analiza y se conserva en el modelo de datos siempre que la fuente la proporcione, pero por ahora no hay interfaz para verla.

El modelo de turnos normalizado

Todos los formatos anteriores se convierten en esto:

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 y confidence solo aparecen cuando la fuente los traía. speaker es null en los turnos sin diarizar, que se muestran como Unassigned con un selector para que los anotadores lo rellenen.

Tres turnos de transcripción con fondo gris rayado, cada uno etiquetado como Unassigned con una flecha desplegable, porque el formato de origen no traía etiquetas de hablante.Los subtítulos automáticos de YouTube llegan sin hablantes, así que cada turno queda como Unassigned hasta que un anotador elige uno

turn_id es la clave de persistencia de las anotaciones por turno y de las asignaciones de hablante. Sale de una cadena turn_id o step_id explícita en la fuente cuando la hay, y si no, de t{índice}. El mismo archivo produce siempre los mismos identificadores, así que las anotaciones sobreviven a una recarga.

Warning: Las unidades de tiempo varían entre herramientas. Whisper y Deepgram usan segundos en coma flotante; AssemblyAI, los desplazamientos de whisper.cpp y la salida TSV de Whisper usan milisegundos enteros. Potato convierte en la frontera para que todo lo que viene después esté en segundos. Si tu propio preprocesado mezcla ambos, los tiempos salen 1000 veces mal.

Archivos adyacentes

Un valor de campo que sea una ruta corta de una sola línea terminada en una extensión de transcripción conocida se lee desde disco en lugar de tratarse como texto. Así, la disposición que ya produjo tu herramienta de ASR, con los archivos de audio junto a su .srt o .json, funciona sin ningún paso de preprocesado:

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

Las rutas se resuelven relativas a task_dir y pasan por la misma validación de seguridad de rutas que cualquier otra ruta configurada, así que un archivo de datos no puede leer fuera del proyecto.

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

Si tus datos contienen realmente transcripciones de una línea que parecen nombres de archivo, desactiva la heurística:

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

Configuración

Visualización: audio_dialogue

La visualización de diálogo con audio muestra los turnos como burbujas de hablante sincronizadas con el audio, con un botón de reproducción en cada turno.

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
OpciónValor por defectoQué hace
audio_keyaudioSubclave del valor del campo que contiene la URL o ruta del audio.
turns_keyturnsSubclave que contiene la lista de turnos. También acepta segments.
speaker_key / text_keyspeaker / textClaves por turno.
speakers[]Lista de {id, name, color, side}. Los hablantes no listados reciben un color determinista y un lado alterno.
allow_speaker_assignmentautoauto activa la asignación por clic cuando hay turnos sin diarizar o una lista que corregir. true la fuerza, false la desactiva.
transcript_is_pathautoSi el valor del campo debe leerse como ruta a un archivo adyacente.
show_timestampstrueMuestra mm:ss–mm:ss en cada turno.
scroll_height480pxAltura del panel de transcripción con desplazamiento.
playback_rates[1, 1.25, 1.5, 2]Opciones del selector de velocidad.

Esquemas

Desde la versión 2.7.1, speech_transcript, voice_interaction y tiered_annotation leen la transcripción directamente del registro de la instancia y aceptan todos los formatos de esta página. Antes solo lo hacía la visualización audio_dialogue.

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

La anotación por capas puede prerrellenar una capa a partir de la transcripción, de modo que los anotadores corrijan una alineación existente en lugar de volver a segmentar el habla a mano:

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"

Las anotaciones sembradas no se escriben hasta que el anotador hace una edición real, así que no se atribuye nada a alguien que solo abrió la instancia.

Construir un archivo de datos con potato transcripts

Apunta el conversor a una carpeta de salida de ASR y escribe un archivo de datos listo para anotar:

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 informa de lo que ha encontrado sin escribir nada, que es la forma más rápida de detectar una carpeta con el archivo de salida de Whisper equivocado:

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.
OpciónPara qué sirve
-o, --outputDónde escribir. Obligatorio salvo con --dry-run.
--formatjson (por defecto) o jsonl.
--media-dirCarpeta de material que emparejar por nombre base.
--media-url-prefixURL base del material en lugar de archivos locales.
--fieldCampo del ítem donde va la transcripción. Por defecto conversation.
--id-prefixCadena que se antepone a cada id generado.
--speaker-keyClave de origen que contiene la etiqueta de hablante.
-r, --recursiveRecorre subcarpetas.
--dry-runInforma del formato detectado y del número de turnos por archivo.
--emit-configAdemás, imprime un fragmento de config.yaml correspondiente.
-q, --quietSilencia el informe por archivo.

Los identificadores de ítem salen del nombre de archivo quitando una extensión de material final, así que interview_01.mp3.json de Whisper se convierte en interview_01 y no en interview_01.mp3.

Exportar de vuelta

Las anotaciones por capas se exportan a ELAN EAF y Praat TextGrid, así que una transcripción puede hacer ida y vuelta: ingerirla, anotarla en Potato, exportarla, refinarla en ELAN o Praat y volver a leer el resultado.

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

Ambos exportadores parten de la salida de tiered_annotation. Los demás esquemas se exportan mediante los formatos estándar.

Resolución de problemas

SíntomaQué ha pasado
Todo aparece como una única burbujaNo se reconoció el formato y cayó en el respaldo de párrafo llano. Ejecuta potato transcripts <file> --dry-run; si informa plain text, los tiempos nunca se analizaron. Normalmente el archivo es un .txt de Whisper, que no tiene tiempos, en lugar del .json o el .srt.
Sin hablantes, todo aparece como UnassignedLa fuente no trae etiquetas de hablante. Whisper por sí solo no diariza, y los subtítulos automáticos de YouTube tampoco. Ejecuta la diarización antes o deja que los anotadores asignen hablantes en la interfaz.
Los tiempos están 1000 veces malSe mezclaron segundos y milisegundos antes. Los offsets de whisper.cpp, AssemblyAI y el TSV de Whisper están en milisegundos.
La ruta de la transcripción aparece como el textoNo se pudo leer el archivo adyacente, así que se mostró la ruta como contenido. Comprueba que se resuelve dentro de task_dir. Los registros del servidor indican el motivo concreto.
Una transcripción de una línea se lee como nombre de archivoPon transcript_is_path: false en el campo de visualización.

Proyecto de ejemplo

examples/audio/transcript-formats/ en el repositorio de Potato muestra seis formatos en paralelo, cada uno cargado desde un archivo adyacente: SubRip, WebVTT, Whisper JSON, YouTube json3, Praat TextGrid y Deepgram. Los seis producen las mismas burbujas de hablante.

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

La misma tarea está disponible como diseño de muestra descargable: Ingesta de formatos de transcripción.

Para detalles de implementación, consulta la documentación de origen.

Lecturas adicionales