cardstream
О проекте

Суть в том, чтобы спрашивать один раз на карту, а не на каждый кадр

Распознавание карт уже работает. Покажите хорошей модели коллекционную карту, и примерно через полсекунды вернутся название, сет и номер. Всё ломается, когда то же самое запускают на видео: не технически, а из-за одного только объёма.

Видео — это в основном повторы. Карта, которую держат перед камерой четыре секунды при пятнадцати кадрах в секунду, — это шестьдесят снимков одной карты, и шестьдесят запросов к эндпоинту распознавания принесут шестьдесят одинаковых ответов. Именно так поступает любой наивный пайплайн для стримов, и потому принято считать, что анализ стримов по карману только платформе с инвестициями.

cardstream нужен, чтобы убрать эти повторы. Это небольшой конечный автомат между вашим видео и запросом на распознавание, и почти все нужные ему сигналы он вычисляет сам: замерла ли сцена, та же ли это карта, что и мгновение назад, есть ли в кадре карта вообще. Запрос уходит, когда замерла действительно новая карта, и только тогда. Всё остальное работает на оборудовании, которое у вас уже есть.

Арифметика

Один запрос на карту, а не на кадр

Час эфира при пятнадцати кадрах в секунду (это верхняя граница частоты, на которой проект проверен) и около сотни разных карт перед объективом. Два способа проанализировать одно и то же видео:

Кадр за кадром 54 000

запросов на распознавание: по одному на кадр, и большинство повторяет вопрос, ответ на который получен мгновение назад.

С cardstream ~100

запросов на распознавание: по одному на каждую новую карту, с какой бы частотой кадров вы ни снимали и сколько бы ни держали каждую карту перед камерой.

Примерно в 540 раз меньше запросов, и этот разрыв — не игра с цифрами, а сама суть архитектуры. Держите карту неподвижно — счётчик не меняется. Поднимите частоту кадров — счётчик не меняется. Число запросов зависит от показанных карт, а не от часов в эфире.

Сопровождение

Кто поддерживает проект

От команды, которая делает распознавание

cardstream создан командой, которая отвечает в Ximilar за распознавание коллекционных предметов. Эндпоинты, к которым он обращается, — те самые, что работают у нас в продакшене, поэтому клиент и сервис поддерживают одни и те же люди.

Работа ведётся открыто

Весь пакет лежит на GitHub: конечный автомат, оба варианта развёртывания и веб-интерфейс. Ничего из логики принятия решений не спрятано за сервисом, в который нельзя заглянуть.

Тесты без сети

Весь набор тестов выполняется офлайн: файлы моделей и HTTP-запросы заменены заглушками. Сделайте форк репозитория на ноутбуке без API-ключа — и всё равно будет видно, не сломалось ли что-нибудь.

Принципы

О чём коду нельзя забывать

  1. Один запрос на карту, а не на кадр

    С этого начинается каждое архитектурное решение. Если сигнал можно вычислить локально, он вычисляется раньше того единственного запроса, который покидает компьютер.

  2. Логика решений в одном экземпляре

    Когда отправлять запрос, решает ровно один модуль. Логика не размазана по драйверу, транспорту и интерфейсу, где три человека изменят её тремя способами: это один файл, который можно прочитать за один присест.

  3. Драйверы остаются тонкими

    Планирование, журналирование и ввод-вывод — дело драйвера, а что считать новой картой — дело движка. Замена способа доставки кадров никогда не меняет втихую момент отправки запроса.

  4. Ничто не блокирует цикл обработки кадров

    Декодирование, детекция, HTTP и диск работают вне цикла. Из-за медленного распознавания пропадёт кадр, но стрим не превратится в растущую задержку.

  5. Без привязки к поставщику, включая нас

    Запрос на распознавание — один заменяемый шаг за небольшим интерфейсом. Все фильтры, ограничения частоты и кэши продолжат работать, даже если направить его совсем в другое место.

  6. По умолчанию всё у вас

    Ваше оборудование, ваш ключ, ваше видео. В клиентском режиме компьютер покидает только один кроп на каждую новую карту, если вы сами не решите сохранять эфир в свой аккаунт Ximilar.

Полевые заметки

Чему нас научили карты

Кое-что здесь настолько противоречит интуиции, что мы записываем это там, где это найдёт следующий человек: в репозитории, рядом с кодом, которого это касается.

Полезная подсказка может стоить точности

Если сообщить эндпоинту, из какой игры карта, отключится его собственный классификатор письменности, и по умолчанию он возьмёт latin. В итоге японская карта незаметно совпадёт со своим английским изданием. Это измерено, а не предположено, и потому указать игру без письменности считается ошибкой.

Кроп с небольшим запасом совпадает лучше

Обрезка вплотную к карте выглядит правильно, а работает хуже. В кропе, который уходит на распознавание, намеренно оставлены поля: немного контекста полезнее чистого края.

Лучше восстановиться, чем делать вид

Когда запрос зависает дольше тайм-аута, пайплайн сам выходит из ступора и идёт дальше, а не ждёт результата, который может не прийти никогда. Это ограничение описано в репозитории, а не замазано.

Исходный код и участие

Читайте, запускайте, присылайте патчи

Репозиторий открыт: один устанавливаемый пакет Python с независимыми extras и веб-интерфейсом. Веса локатора и модели эмбеддингов — наши: мы их обучили и публикуем вместе с релизами под лицензией Apache-2.0. Мы рады issues и пул-реквестам, а особенно отчётам с настоящих эфиров: там всплывает то, чего не покажет ни один тестовый стенд.

Само распознавание — это эндпоинт Ximilar, и так будет всегда: за этой частью стоит база карт. Всё, что вокруг, вы вольны менять.

Открытый код. Работает у вас. Ваш стрим, ваш стек.

Ни платформы-посредника, ни платы за рабочие места: подключите API Ximilar или собственную систему распознавания, и cardstream будет обращаться к ней один раз на каждую новую карту, а не на каждый кадр. Проводите ли вы брейки на Whatnot, ведёте эфир в духе Fanatics Live или продаёте в прямом эфире на своей площадке — запустите cardstream уже сегодня вечером на том оборудовании, которое у вас есть.