Локальный пайплайн, который превращает запись лекции-вебинара (mp4) в transcript.md — текст для написания конспекта: речь сгруппирована по слайдам, содержимое слайдов распознано, формулы в LaTeX, у каждой секции таймкоды и картинка слайда.
Зачем видео, а не только звук: значимая часть лекции часто существует только на слайдах — формулы, схемы, определения, код, термины в правильном написании. Расшифровка одного аудио без слайдов неполна.
Всё работает локально, без внешних API.
MVP. Код, тесты и сквозная интеграция готовы; качество распознавания на целевой машине ещё не проверено.
| Что | Состояние |
|---|---|
Весь пайплайн mp4 → transcript.md на 91-минутной записи |
✅ проходит все стадии (сухой прогон: настоящие стадии, OCR/ASR — заглушки) |
| Извлечение аудио и кадров, детект слайдов, дедупликация, кэш, сборка транскрипта | ✅ проверено на эталонной записи |
| Тесты | ✅ 587 быстрых + 24 на эталонной записи |
| Качество OCR и распознавания речи, VRAM, время на GPU | ⏳ нужна машина с NVIDIA GPU и весами моделей |
Подробная хронология решений — DECISIONS.md, отложенное — TECH_DEBT.md.
audio → vad ─────────────────────────────→ asr → punctuation ──┐
frames → slides → ocr → glossary ─────────────↑ ├→ merge → transcript.md
┘
| Стадия | Что делает | Модель по умолчанию |
|---|---|---|
audio |
WAV 16 кГц моно | ffmpeg |
frames |
кадры 1 кадр/с | ffmpeg (nvdec на GPU) |
slides |
область демонстрации по временно́й дисперсии пикселей, дедупликация кадров по perceptual hash, PNG-кроп последнего кадра каждого слайда | OpenCV, imagehash |
ocr |
печатный текст и формулы слайда в Markdown + LaTeX; рукописное не распознаётся — вместо него ссылка на PNG | PaddleOCR + pix2tex |
glossary |
термины лекции со слайдов для подсказки ASR | — |
vad |
интервалы речи | silero-vad |
asr |
слова с таймкодами | GigaAM-v2 |
punctuation |
пунктуация и регистр (только для ASR без своей пунктуации); таймкоды слов не меняются | sbert_punc_case_ru |
merge |
слияние речи и слайдов по времени, чистка филлеров, абзацы, рендер | — |
Стадии выполняются последовательно, модель выгружается перед следующей стадией — пик VRAM рассчитан ниже 8 ГБ. Результат каждой стадии кэшируется; ключ кэша учитывает входной файл, параметры и код стадии, поэтому правка шаблона вывода пересчитывает только merge.
Альтернативные бэкенды: Whisper large-v3 вместо GigaAM (лекции с англоязычными терминами), Qwen2.5-VL 4bit вместо PaddleOCR + pix2tex.
# Транскрипт лекции
- **Исходный файл:** lecture.mp4
- **Длительность:** 1:31:22
- **ASR-бэкенд:** gigaam-v2
- **OCR-бэкенд:** hybrid
## Слайд 35 — Пример 2. Решите уравнение — 1:09:58–1:10:25
> Пример 2. Решите уравнение
>
> $\sqrt{x^2-x} = -5$
>
> 
Итак, давайте посмотрим на второй пример. Под корнем у нас икс квадрат минус икс…Соседние состояния одного слайда (лектор дописывает решение маркером) объединяются в одну секцию со ссылками на все различающиеся картинки. Короткие паузы демонстрации при смене слайда не рвут текст на отдельные секции.
Требования: Docker с NVIDIA Container Toolkit и NVIDIA GPU с поддержкой CUDA 12. Бюджет памяти пайплайна рассчитан на 8 ГБ VRAM: модели загружаются по одной.
# 1. Собрать образ
docker compose -f docker/compose.yaml build
# 2. Проверить окружение: GPU виден, ffmpeg умеет nvdec, torch видит CUDA
docker compose -f docker/compose.yaml run --rm lecture-transcript verify_env.sh
# 3. Один раз скачать веса моделей в том model-cache (нужна сеть)
docker compose -f docker/compose.yaml run --rm lecture-transcript \
python -m lecture_transcript.warmup
# 4. Положить запись в ./input и запустить
docker compose -f docker/compose.yaml run --rm lecture-transcript \
python -m lecture_transcript.cli /data/input/lecture.mp4 -o /data/output/lectureРезультат: output/lecture/transcript.md и output/lecture/slides/*.png.
После прогрева пайплайн работает без сети: образ по умолчанию запускается с HF_HUB_OFFLINE=1. Каталоги входа и выхода меняются переменными LECTURE_INPUT_DIR и LECTURE_OUTPUT_DIR. Прогрев по умолчанию скачивает модели бэкендов из конфига; --all — все, кроме VLM; --with-vlm — добавить Qwen2.5-VL (~6 ГБ).
Подробности окружения и выбора версий — docker/README.md.
lecture-transcript lecture.mp4 -o out/
lecture-transcript lecture.mp4 -o out/ --asr-backend whisper-large-v3
lecture-transcript lecture.mp4 -o out/ --config my.yaml
lecture-transcript lecture.mp4 -o out/ --slide-region 64,60,1248,656 # если область слайда не определилась
lecture-transcript lecture.mp4 -o out/ --from-stage merge # пересобрать только транскрипт из кэшаОсновные параметры (lecture-transcript --help — полный список):
| Параметр | Назначение |
|---|---|
--asr-backend |
gigaam-v2 (по умолчанию), whisper-large-v3 |
--ocr-backend |
hybrid (по умолчанию), paddle, vlm |
--config |
YAML поверх default_config.yaml |
--slide-region X,Y,W,H |
задать область демонстрации вручную |
--hwaccel |
auto, cuda, videotoolbox, none |
--from-stage, --only-stage |
запуск части стадий |
--no-cache, --cache-dir |
управление кэшем (по умолчанию <out-dir>/.cache) |
Файл без видеодорожки обрабатывается в аудио-режиме: транскрипт без слайдов и предупреждение о неполноте.
Конфигурация разбита по стадиям: media_ingest, slide_extraction, slide_ocr, speech_transcription, transcript_assembly, cache. Значения, подобранные на эталонной записи, прокомментированы прямо в YAML.
Пайплайн без моделей (извлечение, слайды, кэш, сборка) запускается и тестируется на любой машине с ffmpeg:
uv venv --python 3.11
uv pip install -e ".[dev]"
.venv/bin/pytest -m "not gpu and not models and not slow"Веса моделей и GPU-зависимости в pyproject.toml не входят — их набор с версиями живёт в docker/requirements*.txt.
Маркеры тестов:
| Маркер | Что нужно |
|---|---|
reference |
эталонная запись, путь в LECTURE_REFERENCE_MP4 |
slow |
время — минуты |
models |
скачанные веса моделей |
gpu |
CUDA GPU |
Короткие клипы для тестов нарезаются из эталонной записи: python tools/make_fixtures.py. Сами записи в репозиторий не кладутся.
Сухой прогон — весь пайплайн на реальной записи, где OCR, ASR и пунктуация заменены детерминированными заглушками. Проверяет стыки стадий, кэш и формат транскрипта без GPU:
python tools/dry_run.py --input lecture.mp4 --out-dir /tmp/dry_runsrc/lecture_transcript/
contracts.py общие типы данных — стадии общаются только через них
cli.py, config.py оркестрация стадий и конфигурация
cache.py кэш промежуточных артефактов
warmup.py прогрев весов моделей
media_ingest/ аудио и кадры
slide_extraction/ область демонстрации, дедупликация, PNG слайдов
slide_ocr/ OCR-бэкенды, роутинг формул, глоссарий
speech_transcription/ VAD, ASR-бэкенды, пунктуация
transcript_assembly/ слияние, чистка, абзацы, рендер
docker/ образ CUDA + ffmpeg nvdec, зависимости, самопроверка
openspec/ спецификация: proposal, design, specs, tasks
tools/ нарезка фикстур, сухой прогон
- Рукописные пометки маркером не распознаются — транскрипт ссылается на PNG слайда. Это осознанный выбор: картинка полезнее выдуманной моделью формулы.
- Разделения речи по спикерам нет.
- GigaAM не принимает глоссарий как подсказки (hotwords); с ним пайплайн работает без глоссария и предупреждает об этом. Whisper глоссарий получает.
- Детект слайдов и пороги подобраны на одной платформе вебинаров; на другой может понадобиться
--slide-regionи перепроверка порогов. - Генерация самого конспекта в скоуп не входит — пайплайн заканчивается на
transcript.md.
MIT. Лицензия распространяется на код этого репозитория. Модели, которые пайплайн скачивает, распространяются под собственными лицензиями.