Extração de Texto de Vídeo / Áudio
Produza um arquivo de transcrição temporizada ou letras a partir de um arquivo de mídia local (.mp4, .m4s, .mkv, .m4a, .mp3). Lida com o caso comum em que um vídeo baixado não possui faixa de legenda suave, mas tem texto embutido na imagem (hardsub), e com o caso em que você só tem áudio.
Quando usar
- "Extraia as letras / legendas deste vídeo e marque o horário de início de cada linha."
- "Transcreva este áudio com timestamps."
- Arquivos
.m4s/.m4abaixados do Bilibili/YouTube; vídeos de karaokê/AMV/clipes musicais onde as letras são renderizadas na imagem. - Qualquer solicitação que queira uma saída no estilo
.lrcou.srt.
Passo 0 — Inspecione primeiro, sempre
ffprobe -v quiet -print_format json -show_streams -show_format ARQUIVO | python3 -m json.tool
Coisas importantes a descobrir:
- Fluxos presentes.
.m4sdo Bilibili geralmente é só de vídeo (o áudio fica em um.m4s/.m4aseparado). Se não houver fluxo de áudio, encontre o arquivo de áudio correspondente na mesma pasta, combinando a duração. - Legendas suaves?
ffprobe ... -select_streams s— se existir um fluxo de legendas, extraia-o diretamente (ffmpeg -i ARQUIVO -map 0:s:0 out.srt) e pule todo o trabalho de OCR/whisper. Este é o caminho mais rápido. - Duração. Use-a para encontrar o arquivo de áudio correto quando o vídeo não tiver áudio.
- O cartão de título não é a faixa de legenda. O quadro ~0 frequentemente carrega um título grande + sobreposição do artista em uma posição e fonte diferentes das legendas reais. Derivar coordenadas de corte a partir dele resulta em uma faixa que perde todas as letras. Em vez disso, amostre um quadro do meio da música (com uma linha de letra longa visível) — e leia o cartão de título separadamente se precisar do nome da música/artista.
- Confirme qual música antes de transcrever. Um nome de arquivo e um cartão de título podem discordar do que o usuário chama a faixa. Se o áudio/letras não corresponderem ao nome que você espera, diga isso antes de produzir uma transcrição completa — e se o vídeo for uma música entre várias, diga qual você realmente transcreveu.
Passo 1 — Decida a fonte do texto (ordem de prioridade)
- Fluxo de legenda suave → extraia diretamente. Pronto.
- Texto embutido na imagem (hardsub) → OCR nos quadros (Passo 3). Isso fornece o texto oficial e com ortografia correta. Prefira este sempre que o vídeo renderizar visivelmente legendas/letras.
- Apenas áudio, sem texto na imagem → transcrição com whisper (Passo 2).
Se TANTO o texto na imagem quanto uma transcrição de áudio existirem, use o OCR para as palavras e o whisper para o timing/orientação — o texto do OCR é a ortografia autoritativa, o whisper acerta a grade de tempo mas erra gravemente palavras cantadas (por exemplo, ele ouviu "pedofilos" para "pétalas", "frite certo" para uma vogal cantada). Faça verificação cruzada entre si.
Passo 2 — Transcrição de áudio (whisper)
Instale uma vez (veja references/whisper-setup.md para a correção do espelho HF na China continental):
uv venv /tmp/wvenv
/tmp/wvenv/bin/python -m pip install faster-whisper
Extraia WAV mono 16 kHz, opcionalmente normalizado:
ffmpeg -y -i AUDIO -af "loudnorm=I=-16:TP=-1.5:LRA=11,highpass=f=80" -ar 16000 -ac 1 -c:a pcm_s16le /tmp/song.wav
Transcreva (script em scripts/transcribe.py):
HF_HUB_DISABLE_XET=1 HF_ENDPOINT=https://hf-mirror.com \
/tmp/wvenv/bin/python scripts/transcribe.py /tmp/song.wav medium
- O modelo
mediumé o ponto ideal;large-v3é mais preciso, mas lento na CPU. mediumna CPU (int8) transcreve uma música de ~2,5 min em alguns minutos; o processo fica com RSS baixo durante o carregamento do modelo, depois salta para ~1,4 GB RSS enquanto transcreve — não mate em pânico durante a fase de carregamento, verifique/proc/<pid>/statusVmRSS.- NÃO use filtragem VAD para canto.
vad_filter=Trueremove linhas vocais suaves/ reverberadas e pode reduzir uma música de 3 minutos a um punhado de segmentos. Transcreva a faixa inteira. - Ajuste "canto" funcional (sem VAD):
beam_size=5,condition_on_previous_text=True, fallback de temperatura[0.0,0.2,0.4,0.6,0.8,1.0],no_speech_threshold=0.8,log_prob_threshold=-1.5,compression_ratio_threshold=2.8,word_timestamps=True,hallucination_silence_threshold=1.5. (Os padrões descartam frases inteiras e emitem alucinações de silêncio em lacunas instrumentais.)
Passo 3 — OCR de texto embutido (o caminho de alto valor)
Manual completo: references/hardsub-ocr.md. Resumo:
- Instale o tesseract sem sudo extraindo
.debpara um prefixo (detalhes na referência). Correções de symlink paraliblept.so.5elibarchive.so.13estão documentadas lá. - Encontre a região de texto: leia um quadro com
vision_analyzee peça as coordenadas de pixel da faixa de legenda. Legendas geralmente ficam no canto inferior esquerdo em videoclipes — NÃO adivinhe. - Extraia quadros densamente:
ffmpeg -i SRC -vf fps=2 /tmp/frames/f_%04d.png(2 fps → resolução de ~0,5 s; quadro N está em t = N/2 s). - Corte a faixa, aumente a escala 2–3x, tons de cinza, OCR de cada quadro (
--psm 6ou--psm 7). - Deduplicação: quadros consecutivos repetindo uma linha = uma linha de letra; a primeira leitura limpa dá seu tempo de início.
- Texto de baixo contraste: recorra a
vision_analyze. O Tesseract falha em textos serifados claros sobre fundo claro/fotográfico. O modelo de visão o lê de forma confiável — use-o para os quadros em que o tesseract retorna lixo. Este híbrido (tesseract em massa + preenchimento pontual com visão) é o padrão de produção.
Saída
Produza um .lrc quando o usuário pedir letras + timestamps ([mm:ss.xx]linha), caso contrário, MM:SS texto simples. Salve no caminho que o usuário nomear (comumente a Área de Trabalho do Windows quando no WSL, por exemplo /mnt/c/Users/<usuário>/Desktop/).
Entrega: o .lrc que você produz aqui é exatamente a entrada que a habilidade guitar-tabs deseja para seu gráfico de acordes sincronizado com LRC / alinhamento de acordes por linha. Quando uma solicitação de letras seguidas de tablatura vier como uma única tarefa, mantenha o .lrc por perto e alimente-o diretamente em guitar-tabs (seu scripts/chord_analyze.py recebe <áudio> <letras.lrc> e imprime um acorde por linha de letra). As duas habilidades se encadeiam: video-text-extraction → letras temporizadas → guitar-tabs → 弹唱谱.
Armadilhas
.m4sgeralmente é só de vídeo. Não presuma que tem áudio. Corresponda o áudio companheiro pela duração.- Não confie na grafia do whisper para vocais cantados. Verifique com o texto na imagem quando disponível.
- VAD mata vocais suaves — veja o Passo 2.
- O Tesseract precisa de um corte exato. Uma faixa deslocada em ~100px verticalmente retorna ruído. Obtenha coordenadas da visão primeiro.
- As dependências de bibliotecas compartilhadas do Tesseract vêm uma a uma. Cada execução morre com um
error while loading shared libraries: Xdiferente —liblept.so.5, depoislibarchive.so.13, depoislibgif.so.7, e assim por diante. Não desista e não instale um por comando: baixe a lista inteira de uma vez (veja a referência), extraia, depoisldd … | grep "not found"para ver tudo ainda faltando em uma única passagem. Duas pegadinhas extras:libarchive13não tem candidato no Ubuntu mais novo (uselibarchive13t64), eliblept5é o pacote que carrega oliblept.so.5.0.4real — o pacote-devsó fornece symlinks soltos. - Uma leitura curta/confusa do OCR geralmente é um problema de corte, não de OCR. Antes de ajustar
--psmou limites, façavision_analyzeda própria faixa cortada. Se a visão também não vir texto, o corte está errado. Esta única verificação economiza muito ajuste de filtro inútil. - Comandos de terminal com
export/ pipes podem exigir aprovação. Agrupe variáveis de ambiente do tesseract em um script.she executebash script.shem vez deexport ...; tesseract ...inline. - HuggingFace é lento/bloqueado na China continental. Sempre defina
HF_ENDPOINT=https://hf-mirror.comEHF_HUB_DISABLE_XET=1(o backend xet retorna 401 contra o espelho). - Verifique se o download do modelo está realmente progredindo. Uma execução do
faster-whisperque fica parada em ~5 MB RSS e 0 jiffies de CPU está presa buscando o modelo; uma execução real sobe para ~1,4 GB RSS. Inspecione/proc/<pid>/status(VmRSS) em vez de matá-lo ou esperar cegamente.