У нас было 4 нейросети, 2 опенсорсных датасета, 8 000 неразмеченных изображений, 5 333 аугментаций, 392 класса и 4 аккаунта на Roboflow. Не то, чтобы все это было категорически необходимо на проекте, но если уж начал анализировать телевизионное вещание, то к делу надо подходить серьезно. Единственное, что меня беспокоило — это Tesseract.

Меня зовут Саша Аксёнов, я — директор питерской студии разработки Unistory. Вместе с нашим разработчиком Данилой, а потом вместе с ML-инженерами Иваном и Юрой мы пустились в увлекательный трип. 

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

Зачем нужен этот проект? Кому? В чем идея? 

Один из наших клиентов захотел расширить линейку своих продуктов и обратился за помощью к нам. Суть нового продукта — анализ видео на YouTube. Анализировать нужно было, какие бренды упоминают блогеры в своих роликах, какие предметы попадают в кадр, что говорят люди на видео, какую инфу транслируют. 

После таких видео грех не показать пользователю рекламу ваших новых кроссовок
После таких видео грех не показать пользователю рекламу ваших новых кроссовок

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

Например, если вы посмотрели видео об автомобилях, система будет показывать вам рекламные ролики про покупку авто. Если смотрели передачу на тему моды — в течение получаса после этого увидите рекламу одежды. 

Чтобы выполнить задачу, мы решили подключить несколько разных моделей AI. Компьютерное зрение, speech-to-text, OCR, LLM — нам понадобилось всё.

От YouTube до телевидения

Глобальная идея проекта сменилась на ходу. Мы шли к тому, чтобы анализировать YouTube-видео. В один момент заказчик понял, что не сможет подключить наш сервис к видеохостингу. Изначальная идея (скачать видео, проанализировать, показать пользователям рекламу) противоречила бы правилам YouTube.

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

Лицо наших нейросетей, когда они увидят, что показывают по телевизору
Лицо наших нейросетей, когда они увидят, что показывают по телевизору

Как это будет работать:

  • Заказчик, владелец сервиса, заключает контракт с телекоммуникационной компанией

  • Наш API анализирует вещание в режиме реального времени

  • Система подбирает предложения для пользователей на основании контента, который они смотрели

  • Пользователь видит эти рекламные ролики

Такая схема дает большие бизнес-возможности. Например, сервисы с нашим API можно будет продавать по подписке. В этой картине мира каждый маркетолог сможет показывать людям таргетированную рекламу на основе тех передач, которые они посмотрели полчаса назад.

Смотришь «Спасателей Малибу», через 15 минут рекомендации предлагают купить красный купальник

Что мы решили анализировать и какие нейросети использовали

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

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

Поэтому мы решили анализировать все составные части видео по отдельности:

  • Появление логотипов в кадре. На протяжении всего видео, на любом фоне. 

  • Какие объекты попадают в кадр. Одежда, автомобили, еда — вообще всё.

  • Текст, который появляется в кадре, в том числе вшитые субтитры. 

  • Что говорят в тот момент, когда логотип появляется на экране.

  • Сентимент в речи: определяем, что говорит спикер на видео, что-то позитивное или негативное.

YoloV8 — краеугольный камень проекта

Какие нейросети все это делают:

  • Первая YoloV8 находит в кадре предметы

  • Вторая YoloV8, обученная отдельно от первой, находит логотипы в кадре

  • Rev AI анализирует, какой был язык в аудиофрагменте, а затем транскрибирует речь

  • Tesseract распознает статичный текст в кадре

  • Расшифровка текста, сделанная через Rev AI, попадает в Chat GPT, который анализирует ее на позитив / негатив

Какую информацию получает пользователь (читай: маркетолог):

  • Результат визуального анализа. Какие предметы, какие логотипы появлялись в кадре, в каких временных промежутках и как часто.

  • Результат речевого анализа. Имена селебрити и мест (городов, стран), которые были названы. Позитивные и негативные характеристики, которые ведущий давал людям, предметам и брендам.

  • Результат анализа статичного текста. Опять же, позитив / негатив, какие бренды упоминались.  

Сейчас пользователь видит результаты анализа в формате JSON-файла. Не слишком презентабельно, но вполне достаточно, чтобы потом встроить API проекта, к примеру, в мобильное приложение.

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

"transcription": {
			"text": "I say German brands correctly. Adidas, Adidas, Volkswagen, Volkswagen, Mercedes-Benz, Mercedes-Benz, Birkenstock.",
			"contentItems": [
				{
					"value": "Adidas",
					"contentType": "Positive",
					"itemType": "BrandName",
					"timeRanges": [
						{
							"startTime": "00:00:02.9349990",
							"endTime": "00:00:03.3550000"
						},
						{
							"startTime": "00:00:04.1750000",
							"endTime": "00:00:04.5949990"
						}
					]
				},
				{
					"value": "Volkswagen",
					"contentType": "Positive",
					"itemType": "BrandName",
					"timeRanges": [
						{
							"startTime": "00:00:05.3550000",
							"endTime": "00:00:05.8350000"
						},
						{
							"startTime": "00:00:07.3949990",
							"endTime": "00:00:07.8750000"
						}
					]
				},
				{
					"value": "Mercedes-Benz",
					"contentType": "Positive",
					"itemType": "BrandName",
					"timeRanges": [
						{
							"startTime": "00:00:09.1949990",
							"endTime": "00:00:09.8750000"
						},
						{
							"startTime": "00:00:10.9149990",
							"endTime": "00:00:11.5150000"
						}
					]
				},
				{
					"value": "Birkenstock",
					"contentType": "Positive",
					"itemType": "BrandName",
					"timeRanges": [
						{
							"startTime": "00:00:12.1049990",
							"endTime": "00:00:12.7950000"
						}
					]
				}
			]

Одновременный анализ миллиона кадров

На одном из этапов разработки поняли, что есть нереальная просадка по производительности. Что-то было не так, анализ каждого ролика забирал у системы слишком много ресурсов. 

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

Решили проблему просто: поставили ограничение на одновременную обработку не более 30 кадров.

Как долго идет анализ контента

Никакого фронта в продукте нет, только бэкенд. Заказчик планирует использовать продукт как API в разных приложениях и сервисах. 

Сейчас со стороны пользователя проект работает следующим образом. Загружаешь видос в виде файла, система его анализирует. На 10-минутное видео уйдет в среднем 5-6 минут. На время анализа влияет множество факторов, в том числе — количество кадров в секунду. Чем их больше, тем дольше будет идти обработка.

Со временем трудно предсказать: зависит не только от нашего сервера, но и от сторонних серверов, за использование которых мы платим. У них есть свои временные рамки, которые мы не можем контролировать.

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

Нарезаем видосы на кусочки

Были проблемы и с длинными видеороликами. Во время тестов заказчик спокойно мог загрузить 10-часовой файл. Такой файл легко мог положить сервер.

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

«Нарезка» происходит незаметно для пользователя. Человек загружает видео и просто ждет результатов анализа, даже если ролик длится 30 минут минут и весит несколько гигабайт.

Как устроен проект: чуть-чуть архитектуры и микросервисов

Как видите, обработка видео может идти довольно долго. Но мы предусмотрели этот момент на уровне архитектуры и сделали продукт масштабируемым. 

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

Какие сервисы работают сейчас внутри проекта:

  • Один загружает видео

  • Второй формирует очередь из роликов

  • Третий производит анализ

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

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

Где мы взяли датасеты для поиска логотипов и товаров

Наша система должна была находить в видео много всего, но важнее всего были обычные предметы (на языке маркетологов — товары!) и логотипы. Именно объекты и логотипы в кадре позволяют эффективнее всего определить тематику трансляции, а значит — более прицельно предлагать зрителю рекламу. 

Поэтому максимум внимания на проекте — компьютерному зрению.

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

Обучили первую YoloV8 на датасете COCO. Простой датасет с простыми объектами: кошка, автомобиль, бутылка, мяч и т. д.
Обучили первую YoloV8 на датасете COCO. Простой датасет с простыми объектами: кошка, автомобиль, бутылка, мяч и т. д.

Датасет для объектов, предметов, товаров. Мы должны были добиться того, чтобы система находила в кадре самые разные предметы. Например, кастрюли, машины, мебель, обувь. Для этого подошел датасет COCO. 

Как работает обученная нейросеть, можно посмотреть на видео ниже. Как видите, прекрасно находит зонтик и автомобиль — а значит, сможет помочь маркетологам, которые хотят продать людям зонты и машины (ну вдруг!).

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

Датасет для логотипов. Большинство опенсорсных датасетов с логотипами были устаревшими и плохо размеченными. Лучше всего подошел OpenLogo, правда, здесь мы тоже определились не сразу. Долго выбирали среди разных версий датасета, пока не нашли самый подходящий. В нем были материалы для того, чтобы натренировать модель на обнаружение 352 логотипов. 

На видео ниже — демонстрация, как Yolo находит логотипы на видео.

В итоге сервис выдает данные по визуальному анализу примерно так:

	{
		"id": 1,
		"fileName": "4221495b-6323-4cc1-b67b-1653575510c5.mp4",
		"originalFileName": "foo.mp4",
		"detectionItemsResult": {
			"videoDurationInSec": 16,
			"detectionItems": [
				{
					"name": "person",
					"objectId": 0,
					"timeRanges": [
						{
							"startTime": "00:00:00.3333330",
							"endTime": "00:00:15"
						}
					]
				},
				{
					"name": "laptop",
					"objectId": 63,
					"timeRanges": [
						{
							"startTime": "00:00:10.3333330",
							"endTime": "00:00:10.3333330"
						}
					]
				},
				{
					"name": "mercedesbenz",
					"objectId": 47,
					"timeRanges": [
						{
							"startTime": "00:00:09",
							"endTime": "00:00:12"
						}
					]
				},
				{
					"name": "traffic light",
					"objectId": 9,
					"timeRanges": [
						{
							"startTime": "00:00:13.3333330",
							"endTime": "00:00:13.6666660"
						}
					]
				},
				{
					"name": "volkswagen text",
					"objectId": 344,
					"timeRanges": [
						{
							"startTime": "00:00:05.3333330",
							"endTime": "00:00:08.6666660"
						}
					]
				},
				{
					"name": "adidas",
					"objectId": 28,
					"timeRanges": [
						{
							"startTime": "00:00:02.6666660",
							"endTime": "00:00:05"
						}
					]
				},
				{
					"name": "volkswagen",
					"objectId": 146,
					"timeRanges": [
						{
							"startTime": "00:00:05.3333330",
							"endTime": "00:00:08.6666660"
						}
					]
				}
			],

Аугментировали, балансировали, префиксы расставляли

Здесь хороший момент, чтобы рассказать подробнее, как мы работали с датасетом OpenLogo. Внутри датасета 352 класса логотипов — отлично, кто-то уже выполнил за нас большой объем работы. Бери и пользуйся.

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

Каждый логотип в датасете представлен размеченными изображениями. Один класс логотипов, то есть одно новое лого — это один новый класс в датасете. В готовом датасете было 352 логотипа, 352 класса, 27 000 размеченных изображений.

Пример разметки из опенсорсного OpenLogo

Чтобы наша система отвечала требованиям заказчика, расширили опенсорсный OpenLogo на 40 дополнительных классов. Для этого понадобилось добавить 8000 размеченных изображений с логотипами. 

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

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

Так, классы некоторых логотипов изначально были больше других. Считаем: в дефолтном датасете было 352 класса, в целом 27 000 изображений. Значит, в каждом классе должно быть 76 изображений — но это только среднее число. На самом деле, у некоторых логотипов было всего 20 картинок, а на другие могло приходиться по 150 и больше.

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

Не сбалансировали датасет и попали в страну летучих мышей

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

Чтобы справиться с этим, правили исходный код библиотеки, которую использовали для обучения Yolo. Библиотека Ultralytics, готовый фреймворк для обучения Yolo.

Адидас или Ориджиналс?

Среди логотипов, которые должна была уметь детектить модель, было много логотипов с префиксом. Самый стандартный пример, который приходит в голову — Adidas и Adidas Originals.

Для модели компьютерного зрения это абсолютно разные бренды. Поэтому приходилось отдельно искать данные по таким логотипам с префиксом, отдельно их размечать и балансировать.

Когда вы делаете очередной редизайн, где-то в будущем плачет один дата сайентист
Когда вы делаете очередной редизайн, где-то в будущем плачет один дата сайентист

Может ли Tesseract распознать логотип?

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

Вот их мы решили распознавать как текст, а не как изображение — и отдать эту задачу на Tesseract. В нашем JSON'е результат анализа текста внутри видео выглядит так:

"textToVideoResult": {
			"textToVideoItems": [
				{
					"text": "German Brands\nCORRECTLY V\n\ntae Y\n",
					"time": "00:00:01"
				},
				{
					"text": "German Brands\nCORRECTLY V\n\nq\n",
					"time": "00:00:02"
				},
				{
					"text": "wha\nadidas\n\nLS ea\nnn\n=\n\n",
					"time": "00:00:04"
				},
				{
					"text": "wha\n| adidas\n\nm\n\n=\na am\n\n",
					"time": "00:00:05"
				},
				{
					"text": "W))\n\nVolkswagen\n\n",
					"time": "00:00:07"
				},
				{
					"text": "Volkswagen\n\n",
					"time": "00:00:08"
				},
				{
					"text": "&\n\nMercedes-Benz\n\nWass\n",
					"time": "00:00:10"
				},
	
				{
					"text": "BIRKENSTOCK®\n_| BIRKENSTOCK:\n\n",
					"time": "00:00:15"
				}
			],

Как видите, работает Tesseract ни идеально. Залезали в исходный код модели, чтобы исправить ситуацию, но пока серьезных улучшений не добились. Если есть идеи, на что заменить Tesseract — пишите в комментариях!

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

Выехали на бесплатной версии Roboflow

Для наших целей потребовался бы самый дорогой тариф Roboflow. В стремлении сэкономить бюджет заказчика мы решили пользоваться бесплатной версией сервиса.

Зачем тратить, если можно не тратить
Зачем тратить, если можно не тратить

Ограничение бесплатного аккаунта — можно загрузить до 10 000 изображений. Поэтому пришлось повозиться и заводить 4 аккаунта, а потом скачивать все с каждого аккаунта и собирать в единый датасет.

Аугментацию делали локально — опять же, чтобы не оплачивать Roboflow.

На какой стадии сейчас проект. И что будет дальше

От изначальной идеи анализа YouTube-видео мы отказались, но заказчик вовремя разрешил ситуацию и предложил альтернативное видение проекта — анализ телевизионного вещания.

Сейчас мы обучили две YoloV8 (одну для объектов, другую для логотипов), подключили Rev AI и Chat GPT. Собрали все это в единую рабочую систему. Осталось расширить гейтвей и подключить веб-сокет, чтобы добиться возможности анализа TV-трансляций. 


Если хотите узнать дальнейшую судьбу проекта — подписывайтесь на мой Telegram-канал

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

Комментарии (8)


  1. pagin
    22.08.2024 11:11

    А почему не CLIP/DINO/Yolo-world? Для задачи "Определить всё" явно лучше. Ну и как бы получается one-stage. И учить не нужно - берем из коробки.


    1. unistory Автор
      22.08.2024 11:11

      Нам была важна специфичная настройка различных сторон анализа и асинхронность при обработке из-за потенциально огромного количества видео. Поэтому решили не использовать универсальное решение.


  1. Dynasaur
    22.08.2024 11:11

    Почему плачет один дата-сайентист? В чем проблема сказать нейросети, что адидасов бывает три?


    1. unistory Автор
      22.08.2024 11:11

      Не то чтобы это была проблема. Но ведь работы действительно становится больше.


  1. AigizK
    22.08.2024 11:11

    1. анализируете каждый кадр? или применяете какие то оптимизации, например анализ только i-frames

    2. как получаете данные от ТВ


    1. unistory Автор
      22.08.2024 11:11
      +1

      1. Cистема умеет работать в двух режимах: анализ каждого кадра; анализ каждого кратному переданному числу кадра.

      2. Cуществует наш внутренний сервис, ответственный за это. Мы подключаемся к нему по UDP и получаем данные от TV.


  1. MisterClever
    22.08.2024 11:11

    Если не секрет, подскажите пожалуйста, какой нейросетью вы пользовались, когда создавали обложку и арты внутри статьи с отсылками на Страх и ненависть в Лас Вегасе?))) Они просто до мурашек атмосферны и захатывающи)


    1. unistory Автор
      22.08.2024 11:11
      +1

      Спасибо) все банально, Midjourney. Писал промпты вроде « picture in the style of the movie fear and loathing in las vegas, three guys in a car surrounded by bats. »

      Еще хотел для конца статьи сделать такую картинку: терминатор-скелет вылезает из телевизора, при этом у него длинные черные волосы как у Самары из «Звонка». Выдало лютую крипоту.

      Потом отказался от этой идеи и решил просто сделать Самару из «Звонка», вылезающую из телевизора, но милую. Верный способ сделать что-то милое в Midjourney — добавить в промпт «pixar style». В итоге картинки выглядели примерно так: очень милая голова девочки без шеи и тела. Лежит на полу. Оригинал потерял, но вот что-то похожее.