Всем привет, меня зовут Евгений Мунин. Я Senior ML Engineer в Ad Tech в платформе ставок для рекламы и автор ТГ канала ML Advertising. В данной статье мы поговорим об одном из способов повышения узнаваемости брендов в спорте, а точнее виртуальной рекламе. Разберем размещение рекламных баннеров на видео и напишем пример на Python и OpenCV, где разместим логотип Adidas с использованием алгоритма детектирования ключевых точек SIFT и гомографии для искажения баннера под перспективу.
Форматы рекламы в спортивных трансляциях
Для начала сделаем лирическое отступление и ответим на вопрос, а зачем вообще это нужно. Спортивная культура глубоко проникла в повседневную жизнь. От походов на живые матчи до просмотра дома в кругу семьи и друзей, мы эмоционально и финансово вкладываемся в успехи наших команд. Многие из нас стремятся быть похожими на известных спортсменов, вроде Конора Макгрегора или Криштиану Роналду, подражая тому, что они носят или как ведут себя, и готовы покупать бренды, ассоциированные с ними.
Поэтому, как и болельщики, бренды имеют тесную связь с миром спорта. Обладая возможностью достичь миллионов зрителей по всему миру крупные бренды постоянно работают над способами размещения, а издатели над форматами для монетизации. Давайте, кратко пройдемся по основным форматам рекламных креативов в спортивных трансляциях.
Баннеры, билборды, наклейки. Классический формат баннеров в спорте. В зависимости от популярности лиги и ивента стоимость может меняться от 30k$ до X00K$ за игру. Формат не адаптируется под предпочтения ТВ пользователя, поэтому им нельзя персонально таргетировать аудиторию, что снижает эффективность рекламных компаний.

Зеленый экран. Часто используется в бейсболе в MLB. Для зрителей на стадионе это просто пустой экран. Бродкастер (спортивный канал) размещает на нем креативы в видео потоке (аналогично фильмам с CGI). Это относительно дешевый способ интеграций. Бродкастер может легко адаптировать его под аудиторию в разных регионах на пост-обработке видео потока. Проблема этого формата заключается в том, что далеко не каждый стадион оборудован зеленым экраном.

LED баннеры. Периметры полей позволяют размещать LED баннеры с видео креативами, что помогает лучше использовать рекламные площади. Аналогично баннерам, примерная стоимость может варьироваться от 30k$ за игру в зависимости от медийности ивента. Требования к установке могут стать ограничениями. К тому же мы никак не старгетируем ТВ пользователя, тем более того, кто будет смотреть матч в записи.

Overlay. Здесь баннеры накладываются поверх контента. Это достаточно простой способ интегрировать рекламный креатив в готовый видео контент. В частности ранее overlay’и можно было встретить на Youtube. Начиная с 6 апреля 2023 Youtube его отменил. Overlay’и удобны для таргетирования аудиторию, но они очень интрузивные и могут сильно раздражать пользователя. Поэтому издатели накладывают ограничения на размеры баннера и время отображения.

Виртуальная реклама. Бродкастер имеет возможность адаптировать виртуальные креативы на пост-обработке видео под различную аудиторию и регионы. При этом может быть использована любая занятая или пустая рекламная площадь (поле, трибуны, периметр).
Сходу можно предложить несколько случаев ее использования. В случае несколько марок одного сегмента (например кроссовки Nike, Adidas) чтобы максимизировать свой доход, бренды размещают рекламу в одном и том же матче, но в разных регионах. Или например одна и та же марка планирует размещать рекламу своего продукта в разных регионах на разных языках (например в Саудовской Аравии на арабском, в Китае на китайском)
Но такой формат сложно реализовать: требуется детектировать баннер, размещать креатив в перспективе сцены, избегать окклюзий с людьми и предметами и т.д.

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

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

Исторически задачей виртуальных баннеров стали заниматься еще в 2015-17 годах, примерно в это время начали выпускаться статьи на эту тему. В настоящее время, в Европе, есть три основные компании, которые оказывают услуги по размещению виртуальной рекламы для крупных лиг
-
Supponor: делают размещения для футбола (Bundesliga или LaLiga), хоккея (для НХЛ) и с некоторого момента для гандбола. Размещают в основном оверлеи и проводят обработку в реальном времени для трансляций.
-
Sponix: фирма из Катара, занимающаяся рекламой для футбола. Адаптируют свою технологию также для онлайн трансляций и под разные форматы коммуникаций издателя.
-
UniqFeed: позиционируются на теннисе, бадминтоне, бейсболе. В основном те виды спорта, где камера более или менее статична.

Демо: виртуальный баннер Adidas в CS:GO
Давайте, напишем простое демо, в котором разместим логотип Adidas в контексте видео из CS:GO. Для начала загрузим изображение с сценой на карте Dust2.

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

Рассчитаем ключевые точки на текстуре баннера и в сцене. Для этого воспользуемся методом SIFT. Алгоритм ищет характерные точки kp
на входном изображении, а также считает их дескрипторы des
. Обычно, ключевые точки попадают на кромки текстур или букв, а дескрипторы описывают их окружение, как например направление градиента и интенсивность. Алгоритм проходится окном по изображению и сравнивает яркость в каждом пикселе с яркостью в его окрестности. Метод доступен из коробки в OpenCV.
В результате мы получим два списка ключевых точек баннера kp1
и сцены kp2
. Далее нам нужно найти соответствие между дескрипторами с обоих изображений, или другими словами, сматчить полученные точки. Для этого в OpenCV существует метод FlannBasedMatcher. Близость SIFT дескрипторов считается с помощью L2-нормы. FLANN выполняет поиск ближайших соседей и возвращает matches
список сматченных пар ключевых точек с наиболее близкими дескрипторами. k=2
означает, что для каждого дескриптора из des1 мы возвращаем 2 ближайших соседа.
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
# search for matches
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
Далее применим эвристику и из посчитанных матчей отберем те дескрипторы, для которых расстояние до ближайшего соседа меньше, чем 0.7 расстояния до второго соседа. В этом случае, считаем матч хорошим.
good = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good.append(m)
# expects list of lists as matches.
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [[x] for x in good], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)

Далее нам необходимо по найденным сматченным парам ключевых точек сцены и текстуры расчитать матрицу M
перехода из системы координат баннера в систему координат сцены. Такая матрица также называется матрицей гомографии и теория ее нахождения подробно описана в документации OpenCV. Если в двух словах, то это матрица 3х3, которая описывает переход между двумя плоскостями. В OpenCV ее расчет заключен в методе cv2.findHomography
. После ее нахождения мы пересчитываем координаты углов баннера pts
в координаты его углов в сцене dst
.
MIN_MATCH_COUNT = 5
if len(good) > MIN_MATCH_COUNT:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
matchesMask = mask.ravel().tolist()
h, w = img1.shape
pts = np.float32([
[0, 0],
[0, h - 1],
[w - 1, h - 1],
[w - 1, 0],
]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, M)
img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
print("Not enough matches are found", (len(good), MIN_MATCH_COUNT))
matchesMask = None
Также можем отрисовать контур баннера в сцене. Пусть вас не смущает его прямоугольная форма, это не bounding box. Контур строится с помощью cv2.polylines
и в общем случае может быть любым многоугольником неправильной формы.

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

Далее наложим логотип поверх текстуры. Для этого воспользуемся уже посчитанной ранее матрицей гомографии M
. Предварительно поменяем порядок обхода углов логотипа по часовой стрелке. cv2.warpPerspective
поможет нам применить гомографию к каждому пикселю логотипа, и таким образом, исказить его аналогично баннеру в сцене. Далее заполняем черным цветом область внутри полигона под виртуальным баннером, чтобы избежать артефактов на стыках.
# get four corners of the billboard and change their order clock-wise
pts_dst = dst
pts_dst = pts_dst[[0, 3, 2, 1]]
# warp source image
im_temp = cv2.warpPerspective(im_src, M, (im_dst.shape[1], im_dst.shape[0]))
# black out polygonal area in destination image.
cv2.fillConvexPoly(im_dst, pts_dst.astype(int), 0, 16)
Можем отобразить промежуточны искаженный в перспективе логотип im_temp
и сцену с вырезанным полигоном im_dst
.

Далее просуммируем оба массива между собой и получим финальное изображение сцены с наложенным баннером.
im_dst = im_dst + im_temp[:,:,:3]

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

Нужно отметить, что в реальности реализовать качественное наложение не так уж и просто, к примеру:
-
Из-за недостаточного освещения ключевые точки и их дескрипторы в сцене могут задетектиться неправильно, и кадр либо заскипается по количеству матчей меньшим
MIN_MATCH_COUNT
, либо баннер будет сильно искажен. -
Хотя в демо это не заметно, мы никак не обрабатывали окклюзии между игроком и баннером.
Почему SIFT может не справляться?
-
Во-первых, сложные задачи, а порой даже довольно простые сцены, часто вызывают у него затруднения – для корректного срабатывания требуется большое пересечение между кадрами.
-
Во-вторых, алгоритм не устойчив к изменению угла обзора. Даже если мы просто применим аффинное преобразование к изображению — качество упадёт.
-
В-третьих, сложности возникают и с похожими паттернами: на одинаковом паттерне (обои, например) у разных ключевых точек будут одинаковые дескрипторы, из-за чего нормально сопоставить изображения не получится.
Зато SIFT быстрый, доступен из коробки, его легко запустить, и ему не нужен GPU. Поэтому он используется в качестве бейзлайн решения. Далее можно пробовать более сложные модели, например SuperPoint. Также в статье по ссылке можно найти сравнение других методов детектирования ключевых точек.
В целом несмотря на все упрощения, мы прошлись по основным шагам: нахождение ключевых точек и их дескрипторов с использованием SIFT, поиск соответствий между ключевыми точками с помощью FlannBasedMatcher
а также наложение виртуального баннера в сцену.
Итак, в данной статье мы кратко рассмотрели основные форматы рекламы в спорте, узнали, зачем нужна и какие проблемы решает виртуальная реклама, а также написали простое демо замены баннера в сцене с использованием алгоритма детектирования ключевых точек и гомографии для искажения баннера.
Спасибо всем, кто дочитал до конца!
Подписывайтесь на мой канал ML Advertising, чтобы не пропустить другие посты.
Ссылки
Автор: evgenii111