Skip to content

전사 형식

Potato가 읽는 모든 전사·자막 형식. Whisper, WhisperX, Deepgram, AssemblyAI, AWS Transcribe, SRT, WebVTT, TTML, YouTube 자막, CTM, Praat TextGrid, ELAN EAF와 함께 사이드카 파일과 정규화된 발화 차례 모델까지.

Potato는 21가지 전사·자막 입력을 읽고, 무엇을 표시하기 전에 모두 같은 발화 차례 모델로 바꿉니다. 음성 인식 파이프라인이나 자막 도구가 무엇을 만들었든, 변환 스크립트 없이 그대로 Potato를 향하게 할 수 있습니다. 이 문서는 무엇이 되는지, 각 형식이 어떻게 판별되는지, 각각이 무엇을 담고 있는지에 대한 참조입니다. 단계별 설명은 Whisper 전사를 주석하는 방법YouTube 자막을 주석하는 방법에 있습니다.

색으로 구분된 화자 말풍선으로 표시된 전사. 각 말풍선에 재생 버튼과 시각, 차례별 라벨 질문이 붙어 있다.사이드카 SRT 파일이 화자 말풍선으로 표시되고, 각 차례에 질문이 붙어 있습니다

Potato는 전사하지 않습니다

음성 인식화자 분리는 Potato가 무엇을 보기 전에 일어납니다. 먼저 Whisper, WhisperX, pyannote 또는 클라우드 API를 실행하세요. Potato는 그 결과를 받아들입니다. 이 문서의 어떤 것도 모델을 실행하지 않습니다.

Note: Think-Aloud 모드는 실제로 로컬 Whisper 모델을 돌리지만 목적이 다릅니다. 작업하면서 소리 내어 생각하는 주석자를 녹음하는 기능입니다. 이것은 수집 기능이지 전사 취합이 아닙니다.

판별은 확장자가 아니라 내용으로 합니다

Potato는 파일 이름을 믿지 않고 안에 무엇이 있는지 살펴봅니다. captions.txt라는 이름의 WebVTT 파일도 여전히 WebVTT로 해석됩니다. 실질적인 결과는, 같은 전사가 데이터 파일에 직접 들어 있든 디스크의 사이드카 파일에서 읽히든 설정을 바꾸지 않고 동작한다는 것입니다.

지원 형식

음성 인식 출력

형식판별 근거화자단어 단위 시간
Whisper JSONsegments 배열없음있음(--word_timestamps 사용 시)
WhisperX / 화자 분리된 JSONspeaker가 있는 segments있음있음
whisper.cpp JSONtranscription 배열없음없음
Whisper TSVstart/end/text 헤더없음없음
AWS Transcriberesults.items 또는 results.audio_segments있음있음
Deepgramresults.channels 또는 results.utterances있음(diarize=true)있음
AssemblyAItextwords/utterances있음(speaker_labels)있음
Rev.aimonologues 배열있음있음
SPoRCturn_text/turnText있음(추론)없음

자막과 캡션

형식판별 근거화자
SubRip(.srt)조각 화살표, ,mmm 구분자Name: 접두사에서
WebVTT(.vtt)WEBVTT 헤더<v Name> 태그 또는 Name: 접두사에서
SubStation Alpha(.ass, .ssa)[Script Info] / Dialogue:Name 필드에서
TTML / DFXP<tt> 루트 요소speaker/agent 속성에서
YouTube srv1/srv2/srv3<transcript><text> XMLName: 접두사에서
YouTube json3events 배열없음. 자동 생성 자막에는 화자가 없습니다

SubRipWebVTT가 실제로 마주치는 것의 대부분을 덮습니다. TTML은 방송 아카이브와 전문 자막 도구의 내보내기에서 볼 수 있습니다.

정렬과 언어학적 주석

형식판별 근거화자
NIST CTM공백으로 나뉜 열, 숫자 시작 시각과 길이채널 필드에서
Praat TextGridFile type = "ooTextFile"화자마다 한 계층
ELAN EAFANNOTATION_DOCUMENT 루트PARTICIPANT에서, 없으면 계층 ID

TextGrid의 긴 형식과 짧은 형식 모두 해석됩니다. ELAN 파일에서는 Potato가 ALIGNABLE_ANNOTATIONREF_ANNOTATIONTIME_ORDER 표에 대해 해석하고 헤더에서 자료 참조를 읽습니다.

Praat TextGrid가 fieldworker와 speaker라는 두 화자로 표시되고, 계층마다 하나씩이며 침묵 구간은 생략되어 있다.TextGrid의 각 계층이 화자가 되고, 그 사이의 빈 구간은 건너뜁니다

그 밖의 모든 것

세 가지 일반적인 형태가 21가지를 채웁니다.

  • {speaker, start, end, text} 딕셔너리의 단순한 목록.
  • {"audio": ..., "turns": [...]} 객체.
  • 시간 정보가 전혀 없는 평범한 문단. 하나의 말풍선으로 표시됩니다.

지원하지 않는 형식

이들에 대한 파서는 없습니다. 먼저 변환하세요.

  • SAMI(.smi), MicroDVD(.sub), SubViewer(.sbv)
  • Transcriber(.trs), EXMARaLDA, CHAT/CHILDES(.cha)
  • Montreal Forced Aligner와 Gentle의 기본 출력. 그 도구에서 CTM이나 TextGrid로 내보내세요.
  • 특이한 래퍼에 감싸인 Azure Speech, Google Speech-to-Text, Whisper API의 verbose_json
  • 단독 RTTM 화자 분리 파일

단어 단위 신뢰도는 원본에 있으면 해석되어 데이터 모델에 보존되지만, 지금은 이를 보여 주는 화면이 없습니다.

정규화된 발화 차례 모델

위의 모든 형식은 이렇게 바뀝니다.

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
    }
  ]
}

wordsconfidence는 원본에 있었을 때만 나타납니다. 화자 분리가 되지 않은 차례에서는 speakernull이고, 주석자가 채울 수 있도록 선택기와 함께 Unassigned로 표시됩니다.

회색 빗금 배경의 전사 차례 세 개. 원본 형식에 화자 표시가 없어서 드롭다운 화살표와 함께 Unassigned로 표시되어 있다.YouTube 자동 생성 자막은 화자 없이 도착하므로, 주석자가 고를 때까지 모든 차례가 Unassigned로 남습니다

turn_id는 차례별 주석과 화자 배정의 저장 키입니다. 원본에 명시적인 turn_idstep_id 문자열이 있으면 그것을, 없으면 t{색인}을 씁니다. 같은 파일은 언제나 같은 ID를 만들어 내므로 주석은 새로고침해도 남습니다.

Warning: 시간 단위는 도구마다 다릅니다. Whisper와 Deepgram은 부동소수점 초를 쓰고, AssemblyAI와 whisper.cpp의 오프셋, Whisper의 TSV 출력은 정수 밀리초를 씁니다. Potato는 경계에서 변환하므로 그 이후는 모두 초 단위입니다. 직접 만든 전처리에서 둘을 섞으면 시간이 1000배 어긋납니다.

사이드카 파일

알려진 전사 확장자로 끝나는 한 줄짜리 짧은 경로인 필드 값은 텍스트로 취급되지 않고 디스크에서 읽힙니다. 그래서 음성 인식 도구가 이미 만들어 놓은 배치, 즉 자료 파일 옆에 .srt.json이 놓인 구조가 전처리 단계 없이 그대로 동작합니다.

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

경로는 task_dir 기준으로 해석되고 다른 모든 설정 경로와 같은 경로 보안 검증을 거치므로, 데이터 파일이 프로젝트 밖을 읽을 수 없습니다.

인식되는 확장자: .srt .vtt .webvtt .json .json3 .srv1 .srv2 .srv3 .ttml .dfxp .xml .ass .ssa .tsv .ctm .TextGrid .eaf .txt

데이터에 정말로 파일 이름처럼 보이는 한 줄 전사가 들어 있다면 이 추정을 끄세요.

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

설정

표시: audio_dialogue

오디오 대화 표시는 발화 차례를 음성과 동기화된 화자 말풍선으로 그리고, 각 차례에 재생 버튼을 붙입니다.

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
옵션기본값하는 일
audio_keyaudio음성 URL이나 경로가 들어 있는 필드 값의 하위 키.
turns_keyturns차례 목록이 들어 있는 하위 키. segments도 받습니다.
speaker_key / text_keyspeaker / text차례별 키.
speakers[]{id, name, color, side} 목록. 목록에 없는 화자는 결정적인 색과 번갈아 가는 배치를 받습니다.
allow_speaker_assignmentauto미분리 차례나 고칠 목록이 있으면 auto가 클릭 배정을 켭니다. true는 항상 켜고, false는 끕니다.
transcript_is_pathauto필드 값을 사이드카 경로로 읽을지 여부.
show_timestampstrue각 차례에 mm:ss–mm:ss를 표시합니다.
scroll_height480px스크롤되는 전사 창의 높이.
playback_rates[1, 1.25, 1.5, 2]속도 선택기의 항목.

스키마

2.7.1부터 speech_transcript, voice_interaction, tiered_annotation이 모두 인스턴스 레코드에서 직접 전사를 읽고 이 문서의 모든 형식을 받아들입니다. 그 전에는 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

계층 주석은 전사에서 계층을 미리 채울 수 있어서, 주석자가 음성을 처음부터 다시 나누는 대신 기존 정렬을 고치게 됩니다.

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"

미리 채워진 주석은 주석자가 실제로 수정하기 전에는 기록되지 않으므로, 인스턴스를 열어 보기만 한 사람에게 작업이 귀속되지 않습니다.

potato transcripts로 데이터 파일 만들기

변환 도구를 음성 인식 출력 폴더로 향하게 하면 주석 가능한 데이터 파일을 만들어 줍니다.

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은 아무것도 쓰지 않고 무엇을 찾았는지 보고합니다. 엉뚱한 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.
옵션용도
-o, --output쓸 위치. --dry-run이 아니면 필수.
--formatjson(기본) 또는 jsonl.
--media-dir기본 이름으로 짝지을 자료 폴더.
--media-url-prefix로컬 파일 대신 쓸 자료의 기본 URL.
--field전사가 들어갈 항목 필드. 기본값 conversation.
--id-prefix생성되는 모든 ID 앞에 붙일 문자열.
--speaker-key화자 표시가 들어 있는 원본 키.
-r, --recursive하위 폴더까지 훑습니다.
--dry-run파일마다 판별된 형식과 차례 수를 보고합니다.
--emit-config대응하는 config.yaml 조각도 출력합니다.
-q, --quiet파일별 보고를 끕니다.

항목 ID는 파일 이름에서 끝의 자료 확장자를 뗀 것이므로, Whisper의 interview_01.mp3.jsoninterview_01.mp3가 아니라 interview_01이 됩니다.

다시 내보내기

계층 주석은 ELAN EAF와 Praat TextGrid로 내보낼 수 있어서 전사가 한 바퀴를 돌 수 있습니다. 받아들이고, Potato에서 주석하고, 내보내고, ELAN이나 Praat에서 다듬은 뒤 그 결과를 다시 읽어옵니다.

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

두 내보내기 모두 tiered_annotation의 출력을 대상으로 합니다. 다른 스키마는 표준 형식으로 내보냅니다.

문제 해결

증상무슨 일이 일어난 것인가
전체가 하나의 큰 말풍선으로 보임형식을 판별하지 못해 평문 문단 대체 경로로 떨어진 것입니다. potato transcripts <file> --dry-run을 실행해 plain text로 나오면 시간 정보를 애초에 읽지 못한 것입니다. 대개 .json이나 .srt가 아니라 시간 정보가 전혀 없는 Whisper .txt입니다.
화자가 없고 전부 Unassigned원본에 화자 표시가 없습니다. Whisper 단독으로는 화자 분리를 하지 않고 YouTube 자동 생성 자막도 마찬가지입니다. 앞단에서 화자 분리를 실행하거나 주석자가 화면에서 배정하게 하세요.
시간이 1000배 어긋남앞단에서 초와 밀리초가 섞였습니다. whisper.cpp의 offsets, AssemblyAI, Whisper의 TSV는 모두 밀리초입니다.
전사 경로가 본문으로 보임사이드카 파일을 읽지 못해 경로가 내용으로 표시된 것입니다. task_dir 아래에서 해석되는지 확인하세요. 서버 로그에 구체적인 이유가 남습니다.
한 줄짜리 전사가 파일 이름으로 읽힘표시 필드에 transcript_is_path: false를 설정하세요.

예제 프로젝트

Potato 저장소의 examples/audio/transcript-formats/는 여섯 형식을 나란히 보여 줍니다. SubRip, WebVTT, Whisper JSON, YouTube json3, Praat TextGrid, Deepgram이 각각 사이드카 파일에서 읽히고, 여섯 모두 같은 화자 말풍선을 만듭니다.

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

같은 과제를 내려받을 수 있는 쇼케이스로도 제공합니다: Transcript Format Ingestion.

구현 세부 사항은 원본 문서를 보세요.

더 읽을거리