Русский
Русский
English
Статистика
Реклама

Работа с видео

Балет и роботы

04.06.2021 12:23:32 | Автор: admin
Продолжаю рассказывать о своём необычном увлечении. Моё хобби заключается в алгоритмическом преобразовании древнего черно-белого видео в материал, который выглядит современно. Про мою первую работу написано в этой статье. Прошло время, мои навыки улучшились, и теперь я не смеюсь над мемом Zoom and enhance.


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

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

Небольшая история


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

А давайте я вам сейчас расскажу про то, как появился революционный алгоритм оцветнения Deoldify. Даже если вы занимаетесь машинным обучением, не факт, что вы знаете кто такой Джереми Говард (Jеremy Howard). Его профессиональная карьера началась с роли наёмного консультанта, 20 лет назад он занимался тем, что сейчас именуется Data Science, то есть извлечение прибыли из данных с помощью математики.

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

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

image

У Джереми сформировалось четкое понимание того, что настолько мощный инструмент может стать источником роста в любой области. Главная проблема была (и есть) в том, что количество специалистов по самообучающимся системам несопоставимо с количеством проектов, в которых можно было бы задействовать их способности. С его точки зрения гораздо эффективнее было бы дать этот инструмент всем желающим. Так появился проект Fast.Ai, который является симбиозом кода и курса обучения. Код с одной стороны значительно упрощает использование Pytorch (средство для конструирования алгоритмов машинного обучения), а с другой содержит массу готовых приёмов, которыми пользуются профессионалы для повышения скорости и качества обучения. Учебный курс строится по принципу сверху вниз, сначала студенты обучаются использовать готовые конвейеры, затем Джереми показывает как можно написать каждый элемент конвейера с нуля, начиная с живой демонстрации в табличке Excel ключевого алгоритма, лежащего в основе всего Deep Learning. Цель проекта Fast.Ai состоит в том, чтобы научить специалиста из любой области решать типичные задачи на типичных архитектурах (конечно, при наличии способностей к программированию). Чудес не бывает, уровень навыков после такого обучения не превосходит уровень круглое кати квадратное верти, но даже этого достаточно для решения рабочих задач на новом уровне, недоступном коллегам.

В обучающем курсе Fast.Ai одна из тем посвящена использованию архитектуры UNet, которая ориентирована на переинтерпретацию изображений. Например, эту архитектуру можно обучить генерировать реалистичные фотографии из изображений, полученных с тепловизора, или контрастно выделять аномалии на снимках. Если говорить обобщенно, то такая архитектура по известной форме и свойствам, позволяет предсказать наличие у формы свойств, выявление которых было целью обучения.
image
В качестве домашней работы студентам курса предлагалось использовать UNet для решения любой интересной задачи. Некий Jason Antic заинтересовался преобразованием черно-белых фотографий в цветные. Его эксперименты показали, что такая архитектура производит адекватные результаты и есть значительный потенциал для дальнейшего развития. Так появился проект Deoldify, который при содействии самого Джереми Говарда вырос до законченного продукта и в конце концов взорвал интернет. Автор сделал первую версию доступной всем желающим, а сам занялся развитием закрытой коммерческой версии, которую в ограниченном виде можно использовать на генеалогическом проекте MyHeritage.com (требует регистрации, несколько фотографий бесплатно).

image


Кружок Умелые ручки


Главная проблема использования передовых проектов с открытым кодом в области машинного обучения заключается в том, что дружелюбность к пользователю обычно левее нуля. Автор проекта сосредоточен на конвейере обучения, результаты работы алгоритма ему нужны исключительно для презентации сообществу, что нормально, поскольку цель таких проектов это самопиар и вклад в исследования. Самостоятельный допил проектов пользователем является нормой. Чтобы далеко не ходить: перед обработкой видео его необходимо декодировать, обработать каждый кадр и получившееся сжать в видеофайл, если одно видео обрабатывать несколькими инструментами, то после последовательных пережатий про качество можно забыть. Каждый новый инструмент приходится переделывать на работу с пачкой картинок. А что если в инструменте на уровне конвейера вшито использование за один запуск не более 8 кадров? Для демонстрации алгоритма достаточно, для практических целей нет. Придётся писать внешнюю обертку для многократного запуска, ведь маловероятно, что получится изменить чужой конвейер без потери совместимости с предобученным состоянием алгоритма. И, конечно, академические авторы не особо парятся на тему оптимизации. Есть один проект, который отказывался работать с изображениями больше чем спичечный коробок, после оптимизации он стал требовать в 5 раз меньше видеопамяти и теперь переваривает FullHd.

Можно долго перечислять косяки, которые встречались, достаточно остановиться на том факте, что для работы любого алгоритма необходима установка инструментальных библиотек, иногда может потребоваться 2-3 дня экспериментов прежде чем библиотеки перестанут конфликтовать между собой (даже при наличии перечня точных версий существует масса причин почему придётся долго гуглить).

Минутка прекрасного


Выбирать материал для оцветнения не то чтобы очень просто. С одной стороны содержание должно быть любопытно мне, с другой стороны длинный рекламный фильм фирмы Дизеля, насыщенный техническими деталями, вряд ли заинтересует широкую аудиторию, с третьей стороны существуют ограничения в выборе из-за авторских прав. Новые варианты приходят из памяти или в ходе розыска конкретных записей. Мои последние работы посвящены русской балерине Анне Павловой. Про неё достаточно написано и сказано, сохранилось множество фотографий, но так как её профессиональная деятельность связана с движением во времени и пространстве, то самым интересным свидетелем является киноплёнка. К сожалению, часть сохранившихся записей неизвестны широкой публике, а то что сейчас находится поиском совершенно отвратительного качества. Чем интересна фигура Анна Павловой, так это в буквальном смысле фигурой. Её можно считать прообразом эталона современной балерины, возможно, для вас не будет открытием, что, в конце 19-го века, худоба всё ещё коллективно воспринималась как признак болезни либо бедности, конечно, в среде обеспеченных людей встречались разные фигуры, но в целом, упитанность воспринималась, как маркер успешной жизни. Пышущие здоровьем женщины часто выступали на театральной сцене, вот фотографии трёх звёзд того времени.
image

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

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

Проблемы на старте


Декодируем исходное видео в серию файлов PNG. Просматриваем полученные изображения и замечаем, что встречаются кадры, которые повторяют предыдущие.

image

Это стандартная история, ведь на заре кинематографа для экономии пленки использовалась скорость съёмки 12-19 кадров в секунду (далее fps). В более позднюю аналоговую эпоху, когда 99% видеоряда имело скорость 24-25 fps, для демонстрации старых лент использовалось копирование кадр-в-кадр, что приводило к ускоренному воспроизведению. Поэтому в сознании большинства старая хроника прочно ассоциируется с нечеткими мечущимися человечками. Правда заключается в том, что черно-белые оригиналы плёнок очень хорошо сохраняются, даже лучше чем цветные, и имеют разрешение между DVD и FullHd. Всё что вы могли видеть в большинстве случаев было дрянными копиями, переснятыми с проекции на экран. Хотя многие киноленты сохранились лишь на таких копиях (утраты происходят из-за человеческого фактора), всё же количество сохранившихся оригиналов значительно. Доступ к оригиналам имеют только избранные, к счастью, в наши дни компьютерная обработка изображений позволяет неограниченно тиражировать сканированные качественные копии оригиналов, почистить дефекты и воспроизводить материал с нормальной скоростью частоты кадров.

С низкой частотой кадров есть две раздельные проблемы. Во-первых, она нестандартная, если на личном компьютере можно использовать любую скорость воспроизведения, то существует масса случаев, когда необходимо придерживаться диапазона 24-30 fps. Самый простой способ коррекции частоты кадров это через каждые 3-4 кадра повторять последний. Скорость движения объектов при этом становится естественной, но картинка воспринимается как дёрганная, это собственно вторая проблема. В 2021 году технологии позволяют сделать плавную картинку за счет интерполяции кадров. Технология интерполяции кадров в телевизорах и программных видеопроигрывателях начала встречаться примерно с 2005 г. За счет математических алгоритмов два соседних изображения смешиваются так, что при проигрывании возникает ощущение плавного движения в кадре. Это неплохо получается для 24 fps, так как разница между кадрами редко бывает значительной. Но для 12-19 fps такие алгоритмы не годятся: они рисуют смазанное двойное изображение или сумасшедшие артефакты. Эту задачу успешнее решают самообучающиеся алгоритмы, которые способны запоминать как именно следует рисовать промежуточное изображение для разного движения разных типов объектов.

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

Неожиданный поворот


Ручками удалять такое замучаешься, плавали, знаем. Запускаем скрипт обнаружения одинаковых кадров, скрипт вываливается с ошибкой Много совпадений подряд. А, ну, естественно: кадры слишком тёмные, поиск сгребает в одну кучу одинаковые и разные кадры. Запускаем скрипт нормализации динамического диапазона, который автоматически делает контрастными границы, чёрное приводит к чёрному, белое к белому, а затем возвращает на место оттенки серого, которые теряются при подобных манипуляциях.
image

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

image

image

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

Проблема удаления лишних кадров неожиданно стала очень серьезной. На таких тёмных и не насыщенных деталями кадрах нельзя доверять автоматическому поиску дублей, он ошибётся неоднократно, пропустив ненужное и удалив нужное. Прогоняем поиск всех видов дублей на другом эпизоде фильма, в котором количество ошибок будет минимальным. В случае простого повторения, выделив дубли в менеджере файлов, можно понять схему повторения и удалить лишние файлы программно.
image
В этом же случае шаблон выглядел случайно-периодичным, небольшие куски повторяются, но в целом суть неясна. Что теперь делать? Либо отказываться от всей затеи, либо реверсить алгоритм повторения.

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

image

Короткие повторения группируются. Проверяем насколько точно повторяется длинная группа. Группы похожи, но присутствуют небольшие отличия. Это фиаско. Есть несколько возможных причин такой картины: намеренно вносились случайные изменения, или использовалось несколько алгоритмов дополнения кадров, или учитывается содержание кадров, или метод использует не интуитивную функцию. Записывать гораздо более длинную последовательность повторов для математического анализа выглядело как перебор.

image

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

image

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

image


Основная работа


Далее следует 17 операций чёрной магии, в ходе которых на диске образуется 17 папок, содержащих кадры видео после каждой манипуляции. Кроме самого оцветнения, производится автоматическая коррекция неудачно оцветнённых кадров, значительное повышение четкости изображения, восстановленному изображению возвращается аналоговость (чтобы избавиться от ощущения фотошопа), для всего этого применяется 5 разных инструментов улучшения изображений, связанных между собой скриптами, которые туда-сюда переливают каналы яркости и цвета. Названия инструментов останутся моей профессиональной тайной, уж извините, слишком много труда и времени потрачено на собирание этого зоопарка и его модифицирование. Когда я увидел результаты работы Deoldify 2, мне стало понятно, что моё желание быть лучшим в этой области лишено смысла, как бы классно я не выжимал процентики качества, каждый новый подобный алгоритм превосходит старое в разы. Я бросил заниматься оцветнением и погрузился в Machine Learning с целью собрать свой Deoldify, правда, потом произошла череда событий, отвлекших меня от этой цели. В итоге я объединил несколько готовых проектов в общий процесс, результаты работы которого, хоть как-то заменяют мне мой несостоявшийся алгоритм оцветненения. Возможно, в следующей статье я расскажу как использовать оцветнялку от Google, если получится обуздать её аппетит к памяти, там будет код и подробности.

Для завершения работы над видео необходимо постараться убрать косяки, для этого лучше всего подходит профессиональный видеокомбайн Davinci Resolve. Если отдельно открыть следующую картинку, то можно разглядеть количество элементов в цепи ретуширования. Эта конструкция делает фон чёрным, приближает цвета к естесственным, борется с лишними цветами, создаёт имитацию луча прожектора (скрывает незначительные следы использования коррекции).

image

Остаётся сделать интерполяцию кадров, косметическое повышение разрешения до 2K, и вот наше видео готово. Исходная картинка слишком тёмная и чудес ждать не приходится, но зато теперь есть возможность рассматривать плавные движения чёткой фигуры танцовщицы.

Сам же фильм содержит множество сцен с относительно хорошей картинкой, что позволяет оценить насколько сильно алгоритмическая обработка может улучшить изображение. Кадры 2K разрешения слишком большие для статьи, поэтому рядом с полным кадром исходного изображения вставлены уменьшенные в 2 раза финальные кадры.

image


верни картинку взад


image


image


image

Итоги


Сюжет про танец содержит 1251 кадр (до интерполяции), работа заняла 5 дней.
Музыка добавлена из библиотеки бесплатной музыки Youtube.

Фильм содержит 19660 кадров (до интерполяции), обрабатывалось 14 дней (только алгоритмы, ручная ретушь не применялась). С музыкой тут было сложнее, сначала был вариант, собранный из кусочков оперы, которая лежит в основе сценария фильма, но из-за авторских прав не получилось опубликовать эту версию, пришлось использовать подходящие композиции из первой найденной библиотеки, говорят, вышло лучше чем в первый раз.

Характеристики ЭВМ: Amd Ryzen 3 1200, 4 Гб ОЗУ, GTX 1060 3 Гб

- Ссылки на мои работы:

Youtube Не.ЧБ

Rutube Не.ЧБ

Instagram
P.s Не смог удержаться, оцветнил :)

Подробнее..

Перевод Стриминг видео с помощью Akka Streams

12.06.2021 18:20:44 | Автор: admin
Автор статьи, перевод которой мы сегодня публикуем, говорит, что стриминг видео не должен быть такой задачей, с которой у кого-либо возникают сложности. Всё дело в правильном подборе инструментов, среди которых можно отметить пакет Akka Streams. Использование этого пакета позволяет эффективно разрабатывать приложения для потоковой передачи видео.



Правда, не следует думать, что то, о чём мы будем тут говорить, подобно простому примеру, вроде println(Hello world), в котором используется система акторов Akka. Сегодня вы узнаете о том, как создать свой первый сервис для потоковой передачи видео (прошу прощения, если моё предположение неверно, и у вас это уже не первый такой проект). В частности, тут будут использованы пакеты Akka HTTP и Akka Streams, с помощью которых мы создадим REST API, который обладает способностями стриминга видеофайлов в формате MP4. При этом устроен этот API будет так, чтобы то, что он выдаёт, соответствовало бы ожиданиям HTML5-тега <video>. Кроме того, тут я скажу несколько слов о наборе инструментов Akka в целом, и о некоторых его компонентах, вроде Akka Streams. Это даст вам определённый объём теории, которая пригодится вам в работе. Но, прежде чем мы приступим к делу, хочу задать один вопрос.

Почему я решил рассказать о стриминге видео?


У того, что я посвятил эту статью потоковой передаче видео, есть три основных причины.

Первая причина заключается в том, что это, с моей точки зрения, захватывающая и сложная тема. Особенно если речь идёт о крупномасштабных стриминговых сервисах (вроде Netflix). Мне всегда хотелось получше разобраться в этой теме.

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

И третья причина, последняя в моём списке, но такая же важная, как и другие, кроется в том, что разработка стримингового сервиса это очень интересный способ знакомства с библиотекой Akka Streams, которая, на самом деле, значительно упрощает задачу создания подобного сервиса.

А теперь мы можем переходить к нашей основной теме.

Что такое набор инструментов Akka? Удобно ли им пользоваться?


Akka это опенсорсный набор инструментов, который нацелен на упрощение разработки многопоточных и распределённых приложений, и, кроме того, даёт программисту среду выполнения для подобных приложений. Системы, которые базируются на Akka, обычно очень и очень хорошо масштабируются. Проект Akka основан на модели акторов, в нём используется система конкурентного выполнения кода, основанная на акторах. Создатели этого проекта многое почерпнули из Erlang. Инструменты Akka написаны на Scala, но в проекте имеются DSL и для Scala, и для Java.

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

Основной объём кода, который мы будем тут рассматривать, будет использовать пакеты HTTP и Streams. Мы практически не будем пользоваться стандартным пакетом Akka Actors.

Теперь, когда мы разобрались с самыми базовыми сведениями об Akka, пришло время вплотную заняться пакетом Akka Streams.

Что такое Akka Streams?


Akka Streams это всего лишь пакет, построенный на базе обычных акторов Akka. Он нацелен на то, чтобы облегчить обработку бесконечных (или конечных, но очень больших) последовательностей данных. Главной причиной появления Akka Streams стали сложности правильной настройки акторов в системе акторов, обеспечивающей стабильную работу с данными при их потоковой передаче.

Для нас важен тот факт, что в Akka Streams есть встроенный механизм back-pressure (обратное давление). Благодаря этому решается одна из самых сложных проблем мира стриминговой передачи данных. Это настройка правильной реакции поставщика данных на работу в условиях, когда потребитель данных не может справиться с нагрузкой. И эту проблему решает инструмент, которым мы будем пользоваться, а нам остаётся лишь научиться работать с этим инструментом, не вдаваясь в какие-то чрезмерно сложные и запутанные темы.

Пакет Akka Streams, кроме того, даёт в наше распоряжение API, который совместим с интерфейсами, необходимыми для работы с Reactive Streams SPI. И, между прочим, стоит отметить, что сам проект Akka входит в число основателей инициативы Reactive Streams.

Про Akka Streams мы поговорили. Поэтому можем переходить к нашей следующей теоретической теме.

Что такое Akka HTTP?


Akka HTTP это, как и Akka Streams, пакет, входящий в набор инструментов Akka. Этот пакет основан на пакетах Akka Streams и Akka Actors. Он направлен на то, чтобы упростить работу приложений, в которых используются инструменты Akka, с внешним миром по протоколу HTTP. Этот пакет поддерживает и серверные, и клиентские возможности. Поэтому с его помощью можно создавать и REST API, и HTTP-клиентов, которые отправляют запросы к неким внешним сервисам.

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

HTML5-тег <video>


Тег <video> это новый элемент, который появился в HTML5. Он создавался как замена Adobe Video Player. Несложно понять, что главная задача этого HTML-элемента заключается в предоставлении разработчикам возможностей встраивания в HTML-документы медиаплееров, способных воспроизводить видеофайлы. Собственно, этот тег очень похож на <img>.

В теге <video> размещают тег <source>, имеющий два важных атрибута. Первый это src, который используется для хранения ссылки на видео, которое нужно воспроизвести. Второй это type, который содержит сведения о формате видео.

Между открывающим и закрывающим тегом <video> </video> можно ввести какой-то текст, который будет использован в роли текста, выводимого вместо элемента <video> в тех случаях, когда этот элемент не поддерживается браузером. Но в наши дни тег <video> поддерживает даже Internet Explorer, поэтому вероятность возникновения ситуации, в которой может понадобиться подобный текст, стремится к нулю.

Как будет работать стриминг с использованием тега и FileIO?</font></h2>Хотя на первый взгляд кажется, что стриминг с использованием тега <video> и FileIO это нечто архисложное, если в этот вопрос немного вникнуть, окажется, что ничего особенного в нём нет. Дело в том, что то, о чём идёт речь, представлено готовыми блоками, из которых нужно лишь собрать то, что нам нужно.На стороне сервера основная нагрузка ложится на объект <code>FileIO</code>. Он будет генерировать события, содержащие фрагменты файла, потоковую передачу которого мы осуществляем. Конечно, размеры этих фрагментов поддаются настройке. И, более того, настроить можно и позицию, начиная с которой будет осуществляться стриминг файла. То есть видео необязательно смотреть сначала его можно начинать смотреть с любого места, интересующего пользователя. Всё это отлично сочетается с возможностями тега <code><video></code>, выполняющего запрос HTTP GET с заголовком <code>Range</code> для того чтобы включить воспроизведение видео без его предварительной загрузки.Вот пара примеров запросов, выполняемых элементом <code><video></code>:<img src="http://personeltest.ru/aways/lh3.googleusercontent.com/Dfgd_RjgpV9W3jzBF9TVNLT_n8cAf5_VfvhxbfeK5wYH5slwUtXhjQ0FlZ0I79gCWsNgIF1Q3q7nPty5HqpCpgXIYrEtjQINuyMaMHe1nWu228-p8o9Hf3n-cHNZN_5iS0QGktk" align="center"><img src="http://personeltest.ru/aways/lh3.googleusercontent.com/SQPBs8xy8HVsT8cw2_WRoOnQW1dqKRp2WVSAzfFsbCBkSmETbm7NKS8zMLBdwcWdIo1AQDO_tSJJw1_2Tcd_oMoaBzdTwDz62Tq2jtzKX0ujmuCEqo99NKH_YLnlL6FQbZ5UOd0" align="center">Если кому интересно заголовок <code>Range:bytes=x-</code> отвечает за выбор позиции, с которой начинается воспроизведение видео. Первый запрос уходит на сервер в начале воспроизведения видео, а второй может быть отправлен тогда, когда пользователь решит куда-нибудь перемотать видео.Сейчас, после довольно-таки длинного вступления, пришло время заняться кодом.<h2><font color="#3AC1EF">Пишем стриминговый сервис</font></h2>В нескольких следующих разделах я расскажу о реализации серверной части нашего стримингового сервиса. А потом мы создадим простую HTML-страницу, с помощью которой проверим правильность работы этого сервиса.Я люблю делать предположения, поэтому сделаю ещё одно, которое заключается в том, что я ожидают, что тот, кто это читает, владеет основами Scala и SBT.<h3><font color="#3AC1EF">1</font></h3>Добавим в файл <code>build.sbt</code> необходимые зависимости. В этом проекте нам понадобится 3 пакета: <code>akka-http</code>, <code>akka-actor-typed</code> (пакета <code>akka-actor</code>, в теории, достаточно, но никогда нельзя забывать о типобезопасности) и <code>akka-stream</code>.<source lang="scala">libraryDependencies ++= Seq("com.typesafe.akka" %% "akka-actor-typed" % "2.6.14","com.typesafe.akka" %% "akka-stream" % "2.6.14","com.typesafe.akka" %% "akka-http" % "10.2.4")</source><h3><font color="#3AC1EF">2</font></h3>Теперь можно создать главный класс, ответственный за запуск приложения. Я решил расширить класс <code>App</code>. Мне кажется, что это удобнее, чем создавать метод <code>main</code>. На следующем шаге мы поместим сюда код, имеющий отношение к созданию системы акторов и HTTP-сервера.<source lang="scala">object Runner extends App {}</source><h3><font color="#3AC1EF">3</font></h3>После создания главного класса мы можем добавить в него код, о котором говорили выше.<source lang="scala">object Runner extends App {val (host, port) = ("localhost", 8090)implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "akka-video-stream")Http().newServerAt(host, port)}</source>Сейчас нас вполне устроит такая конфигурация. На последнем шаге мы добавим в код вызов метода <code>bind</code>, что позволит открыть доступ к нашему REST API. Тут мы создаём объект <code>ActorSystem</code> с именем <code>akka-video-stream</code> и HTTP-сервер, прослушивающий порт <code>8090</code> на локальном компьютере. Не забудьте о ключевом слове <code>implicit</code> в определении системы акторов, так как подобный неявный параметр необходим в сигнатуре метода <code>Http</code>.<h3><font color="#3AC1EF">4</font></h3>А тут мы, наконец, реализуем конечную точку REST API, используемую для обработки запросов от тега <code><video></code>.<source lang="scala">object Streamer {val route: Route =path("api" / "files" / "default") {get {optionalHeaderValueByName("Range") {case None =>complete(StatusCodes.RangeNotSatisfiable)case Some(range) => complete(HttpResponse(StatusCodes.OK))}}}}</source>Как видите, я создал конечную точку с URL <code>api/files/default/</code>. В её коде проверяется, есть ли в запросе заголовок <code>Range</code>. Если он содержит корректные данные сервер возвращает ответ с кодом <code>200</code> (<code>OK</code>). В противном случае возвращается ответ с кодом <code>416</code> (<code>Range Not Satisfiable</code>).<h3><font color="#3AC1EF">5</font></h3>Пятый шаг нашей работы отлично подходит для реализации метода, ради которого, собственно, и была написана эта статья.<source lang="scala">private def stream(rangeHeader: String): HttpResponse = {val path = "path/to/file"val file = new File(path)val fileSize = file.length()val range = rangeHeader.split("=")(1).split("-")val start = range(0).toIntval end = fileSize - 1val headers = List(RawHeader("Content-Range", s"bytes ${start}-${end}/${fileSize}"),RawHeader("Accept-Ranges", s"bytes"))val fileSource: Source[ByteString, Future[IOResult]] = FileIO.fromPath(file.toPath, 1024, start)val responseEntity = HttpEntity(MediaTypes.`video/mp4`, fileSource)HttpResponse(StatusCodes.PartialContent, headers, responseEntity)}</source>Тут я сделал следующее:<ul><li>Загрузил файл, потоковую передачу которого я хочу организовать, а затем, учитывая заголовок из запроса, и данные о файле, нашёл позицию в файле, с которой начнётся стриминг, а так же сформировал заголовок <code>Content Range</code>.</li><li>С помощью <code>FileIO</code> создал поток из ранее загруженного файла. Затем я использовал этот поток в роли данных в <code>HttpEntity</code>.</li><li>Я создал ответ, <code>HttpResponse</code>, с кодом <code>206</code> (<code>Partial Content</code>), с соответствующими заголовками и с телом в виде <code>responseEntity</code>.</li></ul>Ещё мне хочется подробнее поговорить о <code>FileIO</code>, так как это самый удивительный механизм во всей статье. Что, на самом деле, происходит при выполнении строки <code>FileIO.fromPath(file.toPath, 1024, start)</code>?Тут, из содержимого файла, находящегося по заданному пути, создаётся объект <code>Source</code> (Akka-аналог Producer из Reactive Streams). Каждый элемент, выдаваемый этим объектом, имеет размер, в точности равный 1 Мб. Первый элемент будет взят из позиции, указанной в параметре <code>start</code>. Поэтому, если в <code>start </code>будет 0 первый элемент окажется первым мегабайтом файла.<h3><font color="#3AC1EF">6</font></h3>Мы уже реализовали основную логику серверной части приложения. А сейчас нам надо отрефакторить её код для того чтобы нашим сервером можно было бы пользоваться.Начнём с внесения изменений в определение REST API:<source>complete(HttpResponse(StatusCodes.Ok)) => complete(stream(range))</source>Получается, что вместо того, чтобы просто возвращать <code>OK</code>, мы вызываем метод <code>stream</code> с передачей ему параметра <code>range</code> и начинаем стриминг.Нельзя забывать о том, что наш API всё ещё недоступен для внешнего мира. Поэтому нам нужно модифицировать соответствующий фрагмент кода, ответственный за запуск HTTP-сервера:<source>Http().newServerAt(host, port) =>Http().newServerAt(host, port).bind(Streamer.route)</source>Готово! Теперь у нас есть рабочий бэкенд, а наш REST API ждёт подключений от любых программ, которым он нужен. Осталось лишь всё протестировать.<h2><font color="#3AC1EF">Тестирование стримингового сервиса</font></h2>Мы, чтобы протестировать приложение, создадим простую HTTP-страницу, единственным достойным внимания элементом которой будет тег <code><video></code>. Причём, обо всём, что надо знать для понимания работы этой страницы, мы уже говорили. Поэтому я просто приведу ниже полный код соответствующего HTML-документа.То, что вы в итоге увидите в окне браузера, открыв эту страницу, должно, более или менее, напоминать то, что я покажу ниже. Конечно, ваш стриминговый сервис вполне может передавать не тот видеофайл, который использовал я.<source lang="html"><!DOCTYPE html><html lang="en"><head><title>Akka streaming example</title></head><body style="background: black"><div style="display: flex; justify-content: center"><video width="1280" height="960" muted autoplay controls><source src="http://personeltest.ru/away/localhost:8090/api/files/default" type="video/mp4">Update your browser to one from XXI century




Тут мне хотелось бы обратить ваше внимание на 5 важных вещей:

  1. Я использовал возможности тега <source> вместо использования соответствующих атрибутов тега <video>.
  2. В атрибуте src тега <source> я указал путь, при обращении к которому бэкенд начнёт потоковую передачу видеофайла.
  3. В атрибуте type тега <source> я указал тип файла.
  4. Я добавил к тегу <video> атрибуты autoplay и muted для того чтобы видео начинало бы воспроизводиться автоматически.
  5. К тегу <video> я добавил и атрибут controls, благодаря чему будут выводиться элементы управления видеоплеера, встроенного в страницу.

На элемент <div> можете особого внимания не обращать. Он тут нужен лишь для стилизации плеера.

Для проверки правильности работы системы достаточно запустить бэкенд и открыть вышеописанный HTML-документ в любом современном браузере.


Правильная работа стримингового сервиса

Обратите внимание на то, что автоматическое воспроизведение видео не начнётся до тех пор, пока к тегу <video> не будут добавлены атрибуты muted и autoplay. Если не оснастить тег <video> этими атрибутами воспроизведение придётся включать вручную, нажимая на соответствующую кнопку.

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

Что можно улучшить?


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

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

Итоги


Сегодня я постарался доказать то, что реализация простого стримингового сервиса это, при условии использования правильных инструментов, проще, чем кажется. Использование инструментов Akka и подходящего HTML-тега способно значительно сократить объём работы. Правда, не забывайте о том, что тут показан очень простой пример. Для реализации реального стримингового сервиса этого может быть недостаточно.

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

Вот GitHub-репозиторий с кодом проекта.

Какие инструменты вы выбрали бы для создания собственного стримингового сервиса?


Подробнее..

Стриминг множества RTSP IP камер на YouTube иили Facebook

07.06.2021 18:08:54 | Автор: admin

Как известно, у YouTube отсутствует фича захвата RTSP потока. Возможно, это сделано не случайно, а исходя из голой прагматики, чтобы люди не повесили на YouTube статическое видеонаблюдение за своими подъездами и не утилизировали его каналы, которые, как оказалось в пандемию, вовсе не резиновые. Напомним, что некоторое время назад имели место истории с ухудшением и ограничением качества стримов до 240p. Или есть еще одно предположение: стримы с IP камер это зло для YouTube, потому что у них чуть более ноля зрителей, на которых не накрутишь миллион просмотров рекламы. Так или иначе, фича не представлена, и мы постараемся заполнить этот пробел - помочь YouTube осчастливить пользователей.

Допустим, мы хотим взять обычную уличную IP камеру, которая отдает H.264 поток по RTSP и перенаправить ее на YouTube. Для этого потребуется принять RTSP поток и сконвертировать в RTMPS поток, который принимает YouTube. Почему именно в RTMPS, а не RTMP? Как известно несекьюрные протоколы отмирают. HTTP предан гонениям, и его участь постигла другие протоколы, не имеющие буквы S - значит Secure на конце. От RTMP потока отказался Facebook, но спасибо, оставил RTMPS. Итак, конвертируем RTSP в RTMPS. Делаем это Headless способом (без использования UI), т.е. на сервере.

Для одного RTSP потока потребуется один YouTube аккаунт, который будет принимать поток. Но что делать если камер не одна, а много?

Да, можно насоздавать вручную несколько YouTube-аккаунтов, например, чтобы покрыть видеонаблюдением приусадебный участок. Но это с огромной вероятностью нарушит условия пользовательского соглашения. А если камер не 10, а все 50? Создавать 50 аккаунтов? А дальше что? Смотреть это как? В этом случае на помощь может прийти микшер, который объединит камеры в один поток.

Посмотрим, как это работает на примере двух RTSP камер. Результирующий поток mixer1 = rtsp1 + rtsp2. Отправляем стрим mixer1 на YouTube. Все работает - обе камеры идут в одном потоке. Здесь стоит заметить, что микширование - достаточно ресурсоемкая по использованию CPU операция.

При этом, так как мы уже имеем RTSP поток на стороне сервера, мы можем перенаправить этот поток на другие RTMP endpoints, не неся при этом дополнительных расходов по CPU и памяти. Просто снимаем трафик с RTSP стрима и тиражируем на Facebook, Twitch, куда угодно без дополнительного RTSP захвата и депакетизации.

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

Например, с помощью запроса:

/rtsp/startup

можно захватить видеопоток от IP камеры.

Запрос:

/rtsp/find_all

позволит получить список захваченных сервером RTSP потоков.

Запрос для завершения RTSP сессии выглядит так:

/rtsp/terminate

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

Давайте подробно рассмотрим, как это можно сделать.

Небольшой мануал, как с помощью минимального кода организовать Live трансляцию на YouTube и Facebook

В качестве серверной части мы используем demo.flashphoner.com. Для быстрого развертывания своего WCS сервера воспользуйтесь этой инструкцией или запустите один из виртуальных инстансов на Amazon, DigitalOcean или в Docker.

Предполагается, что у вас имеется подтвержденный аккаунт на YouTube и вы уже создали трансляцию в YouTube Studio, а так же создали прямую видеотрансляцию в своем аккаунте на Facebook.

Для работы Live трансляций на YouTube и Facebook нужно указать в файле настроек WCS flashphoner.properties следующие строки:

rtmp_transponder_stream_name_prefix= Убирает все префиксы для ретранслируемого потока.

rtmp_transponder_full_url=true В значении "true" игнорирует параметр "streamName" и использует RTMP адрес для ретрансляции потока в том виде, в котором его указал пользователь.

rtmp_flash_ver_subscriber=LNX 76.219.189.0 - для согласования версий RTMP клиента между WCS и YouTube.

Теперь, когда все подготовительные действия выполнены, перейдем к программированию. Разместим в HTML файле минимально необходимые элементы:

Подключаем скрипты основного API и JS скрипт для работы live трансляции, который мы создадим чуть позже:

 <script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="rtsp-to-rtmp-min.js"></script>

Инициализируем API на загрузку web-страницы:

<body onload="init_page()">

Добавляем нужные элементы и кнопки поля для ввода уникальных кодов потоков для YouTube и Facebook, кнопку для републикации RTSP потока, div элемент для вывода текущего статуса работы программы и кнопку для остановки републикации:

<input id="streamKeyYT" type="text" placeholder="YouTube Stream key"/><input id="streamKeyFB" type="text" placeholder="FaceBook Stream key"/><button id="repubBtn">Start republish</button><div id="republishStatus"></div><br><button id="stopBtn">Stop republish</button>

Затем переходим к созданию JS скрипта для работы републикации RTSP. Скрипт представляет собой мини REST клиент.

Создаем константы:

Константа "url", в которую записываем адрес для запросов REST API . Замените "demo.flashphoner.com" на адрес своего WCS.

Константа "rtspStream" указываем RTSP адрес потока с IP камеры. Мы для примера используем RTSP поток с виртуальной камеры.

var url = "https://demo.flashphoner.com:8444/rest-api";var rtspStream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"

Функция "init_page()" инициализирует основной API при загрузке web - страницы. Так же в этой функции прописываем соответствие кнопок вызываемым функциям и вызываем функцию "getStream", которая захватывает RTSP видеопоток с IP камеры:

function init_page() {Flashphoner.init({});repubBtn.onclick = streamToYouTube;stopBtn.onclick = stopStream;getStream();}

Функция "getStream()" отправляет на WCS REST запрос /rtsp/startup который захватывает видеопоток RTSP адрес которого был записан в константу rtspStream

function getStream() {    fetchUrl = url + "/rtsp/startup";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "uri": rtspStream        }),    }    fetch(fetchUrl, options);console.log("Stream Captured");}

Функция "streamToYouTube()" републикует захваченный видеопоток в Live трансляцию на YouTube:

function streamToYouTube() {fetchUrl = url + "/push/startup";const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "streamName": rtspStream,"rtmpUrl": "rtmp://a.rtmp.youtube.com/live2/"+document.getElementById("streamKeyYT").value        }),    }  fetch(fetchUrl, options);streamToFB()}

Эта функция отправляет на WCS REST вызов /push/startup в параметрах которого передаются следующие значения:

"streamName" - имя потока, который мы захватили с IP камеры. Имя потока соответствует его RTSP адресу, который мы записали в константу "rtspStream"

"rtmpUrl" - URL сервера + уникальный код потока. Эти данные выдаются при создании Live трансляции в YouTube Studio. В нашем примере мы жестко закрепили URL в коде, вы можете добавить для него еще одно поле на свою web страницу. Уникальный код потока указывается в поле "streamKeyYT" на нашей Web странице.

Функция "streamToFB" републикует захваченный видеопоток в Live трансляцию на Facebook:

function streamToFB() {fetchUrl = url + "/push/startup";const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "streamName": rtspStream,"rtmpUrl": "rtmps://live-api-s.facebook.com:443/rtmp/"+document.getElementById("streamKeyFB").value        }),    }  fetch(fetchUrl, options);document.getElementById("republishStatus").textContent = "Stream republished";}

Эта функция так же отправляет на WCS REST вызов "/push/startup" в параметрах которого передаются значения:

"streamName" - имя потока, который мы захватили с IP камеры. Имя потока соответствует его RTSP адресу, который мы записали в константу "rtspStream"

"rtmpUrl" - URL сервера + уникальный код потока. Эти данные можно найти на странице Live трансляции в Facebook в секции Live API. Url сервера в этой функции мы указали в коде, как и для функции републикации на YouTube. Уникальный код потока берем из поля "streamKeyFB" на Web странице.

Функция "stopStream()" отправляет RTSP запрос "/rtsp/terminate" который прекращает захват потока с IP камеры на WCS и соответственно прекращает публикации на YouTube и Facebook:

function stopStream() {fetchUrl = url + "/rtsp/terminate";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "uri": document.getElementById("rtspLink").value        }),    }    fetch(fetchUrl, options);document.getElementById("captureStatus").textContent = null;document.getElementById("republishStatus").textContent = null;document.getElementById("stopStatus").textContent = "Stream stopped";}

Полные коды HTML и JS файлов рассмотрим немного ниже.

Итак. Сохраняем файлы и пробуем запустить.

Последовательность действий для тестирования

Создаем Live трансляцию в YouTube Studio. Копируем уникальный код видеопотока:

Открываем созданную ранее HTML страницу. Указываем в первом поле уникальный код видеопотока, который мы скопировали на YouTube:

Создаем Live трансляцию в своем аккаунте на Facebook. Копируем уникальный код видеопотока.

Возвращаемся на нашу web страничку, вставляем скопированный код во второе поле и нажимаем кнопку "Start republish

Теперь проверяем работу нашей републикации. Снова переходим в YouTube Studio и на Facebook, ждем несколько секунд и получаем превью потока.

Для завершения републикации нажмите кнопку "Stop"

Теперь, как и обещали, исходные коды примера полностью:

Листинг HTML файла "rtsp-to-rtmp-min.html"

<!DOCTYPE html><html lang="en">    <head>        <script type="text/javascript" src="../../../../flashphoner.js"></script>        <script type="text/javascript" src="rtsp-to-rtmp-min.js"></script>    </head>    <body onload="init_page()">        <input id="streamKeyYT" type="text" placeholder="YouTube Stream key" /> <input id="streamKeyFB" type="text" placeholder="Facebook Stream key" /> <button id="repubBtn">Start republish</button>        <div id="republishStatus"></div>        <br />        <button id="stopBtn">Stop republish</button>    </body></html>

Листинг JS файла "rtsp-to-rtmp-min.js":

var url = "https://demo.flashphoner.com:8444/rest-api";var rtspStream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"function init_page() {    Flashphoner.init({});    repubBtn.onclick = streamToYouTube;    stopBtn.onclick = stopStream;    getStream();}function getStream() {    fetchUrl = url + "/rtsp/startup";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "uri": rtspStream        }),    }    fetch(fetchUrl, options);    console.log("Stream Captured");}function streamToYouTube() {    fetchUrl = url + "/push/startup";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "streamName": rtspStream,            "rtmpUrl": "rtmp://a.rtmp.youtube.com/live2/" + document.getElementById("streamKeyYT").value        }),    }    fetch(fetchUrl, options);    streamToFB()}function streamToFB() {    fetchUrl = url + "/push/startup";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "streamName": rtspStream,            "rtmpUrl": "rtmps://live-api-s.facebook.com:443/rtmp/" + document.getElementById("streamKeyFB").value        }),    }    fetch(fetchUrl, options);    document.getElementById("republishStatus").textContent = "Stream republished";}function stopStream() {    fetchUrl = url + "/rtsp/terminate";    const options = {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify({            "uri": rtspStream        }),    }    fetch(fetchUrl, options);    document.getElementById("republishStatus").textContent = "Stream stopped";}

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

Удачного стриминга!

Ссылки

Наш демо сервер

WCS на Amazon EC2 - Быстрое развертывание WCS на базе Amazon

WCS на DigitalOcean - Быстрое развертывание WCS на базе DigitalOcean

WCS в Docker - Запуск WCS как Docker контейнера

Трансляция WebRTC видеопотока с конвертацией в RTMP - Функции сервера по конвертации WebRTC аудио видео потока в RTMP

Трансляция потокового видео с профессионального устройства видеозахвата (Live Encoder) по протоколу RTMP - Функции сервера по конвертации видеопотоков от Live Encoder в RTMP

HTML5-трансляции с RTSP-IP камер - Функции сервера по воспроизведению RTSP видеопотоков

Подробнее..

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

31.03.2021 14:23:38 | Автор: admin

Group-IB в шестой раз оценила рынок интернет-пиратства в России в 2020 году он составил $59 млн, что на 7% ниже показателей 2019 года. Пираты так и не смогли восстановить базу видеоконтента после ликвидации Большой тройки CDN, потеряли на рекламе и вступили в борьбу за зрителя с легальными онлайн-кинотеатрами, нарастившими аудиторию в условиях пандемии.

И сервер гнется и скрипит

В ковидный 2020 год общая аудитория легальных видеосервисов, по данным ТМТ Консалтинг, по сравнению с предыдущим годом выросла на 17%, до 63млн зрителей, а сам рынок официальных онлайн-кинотеатров увеличился на 66% до 27,8млрд руб.

Росло и желание россиян посмотреть пиратку количество запросов впопулярных поисковых системах напросмотр бесплатных фильмов исериалов поитогам года выросло на 12% и составило 11,8 млрд (в 2019 году было 10,5 млрд). Апрель 2020 года и вовсе поставил абсолютный исторический рекорд по числу намерений бесплатно посмотреть пиратские фильмы 1,4 млрд. Из-за большого наплыва зрителей серверы пиратов даже не выдерживали нагрузок.

Несмотря на рост интереса к пиратке, теневой рынок видео-контента в прошлом году продолжил сжиматься, потеряв 7%: с $63,5млн. в 2019 году он достиг отметки $59 млн в 2020 году. Летом Group-IB выпустила исследование С блэкджеком и шлюпками, в котором назвала основными бенефициарами рынка интернет-пиратства онлайн-казино и букмейкерские конторы. Именно они спонсируют нелегальное копирование кинопремьер и сериалов, переводы на несколько языков студиямиозвучивания, а после публикации ворованного контента размещают в пиратских онлайн-кинотеатрах рекламные баннеры, промокоды и ссылки для привлечение новых игроков. Пристальное внимание правоохранительных органов, финансовых учреждений и регуляторов к теневым финансовым потокам привело к тому, что интерес рекламодателей к пиратке начал падать: средний CPM (Cost-Per-Mille, цена за1000показов в рекламной кампании) за год снизился примерно на 16% c $6 до $5 по сравнению с показателями 2019 года.

Впервые специалисты направления Group-IB Anti-Piracyоценили объем российского рынка видео-пиратства в2015году: тогда онсоставил $32млн, в2016 году онвырос практически вдвое до$62млн, в2017 году $85 млн (рост+21%), в2018 году $87 млн (рост +2,3%). Однако в2019 году впервые запоследние пять лет произошел резкий обвал: заработок интернет-пиратов загод сократился на27% и в 2020 году продолжил сжиматься.

CDN второй волны

К середине 2020 года на смену трем крупнеишим пиратским видеобалансерам (CDN) Moonwalk, HDGO и Kodik, ликвидированным в 2019 году, пришли CDN второй волны: Collaps, HDVB, VideoCDN, Videoframe, Bazon, Ustore, Alloha, Protonvideo. Однако даже сейчас база нелегального видеоконтента этих восьми крупнейших CDN содержит лишь 50% от объемов Большой тройки, снабжавших 75 000 фильмами и сериалами до 90% пиратских онлайн-кинотеатров России и СНГ.

Очередной технологической новинкой, взятой на вооружение пиратами, стала интеграция видеобалансеров с полностью автоматизированными онлайн-кинотеатрами. Первым массовым прототипом стал скрипт Cinemapress, на базе которого в 2018-2019 гг появилось около 400 пиратских онлайн-кинотеатров. В апреле 2020 года на смену устаревшему проекту Cinemapress пришел Yobobox c 250 доменами, который в отличие от предшественника полностью интегрирован с одним из крупнейших CDN Collapse, и предлагает бесплатный доступ ко всей базе видеоконтента.

Поисковик Яндекс по-прежнему остается главным источником для пиратских онлайн-кинотеатров, на него приходится в среднем до 90% трафика, хотя по состоянию на начало ноября 2020 г этот показатель снизился на 5 процентных пунктов по сравнению с уровнем 2019 г. Оставшиеся 10% обеспечивают другие поисковики (на Google приходится до 2%), соцсети, мессенджеры, надстройки в браузерах и прямые переходы. Кроме того, тенденция ускорения индексирования новых ссылок поисковым ботом Яндекса и методы автоматизации, которые используют пираты, могут нивелировать эффект удаления из поисковой выдачи без применения систем высокочастотного мониторинга.

Особую тревогу у экспертов вызывает тот факт, что в 2020 году пираты научились оперативно отслеживать удалённые при помощи антипиратского меморандума ссылки и в автоматическом режиме генерировать новые дубли (с другим URL), что значительно снижает эффективность борьбы. Напомним, что подписанный в 2018 году антипиратский меморандум, с участием Яндекса, Mail.Ru, Rambler, Первого канала, ВГТРК, СТС Медиа, Газпром-медиа, обязывал поисковики удалять ссылки на страницы с пиратским контентом из результатов выдачи по поисковым запросам пользователей, и до недавнего времени являлся эффективным средством противодействия интернет-пиратству.

В 2020 году пираты, как и легальные онлайн-кинотеатры существенно увеличили аудиторию, но не смогли извлечь из этого максимальной выгоды, замечает Андрей Бусаргин, заместитель генерального директора Group-IB по направлению защиты от цифровых рисков Мы видим, как пережив падение большой тройки CDN, пираты восстанавливают свои технические мощности и усиливают противодействие правообладателям. Некоторые из злоумышленников, используя мутирующие ссылки-дубли, смену домена и децентрализованные CDN, нашли способы, как обойти антипиратский меморандум, полностью обесценив попытки ручного регулирования и методы борьбы с пиратством, актуальные еще несколько лет назад

Эксперты Group-IB уверены, что классического мониторинга иблокировки уже недостаточно необходимо выявлять иблокировать инфраструктуру преступных групп, используя автоматизированную систему для поиска иустранения цифровых рисков, база знаний которой ежедневно дополняется данными обинфраструктуре, тактике, инструментах иновых схемах пиратов.

Подробнее..

KODI собираем удобный и функциональный медиацентр для дома. Часть 6. Синхронизация медиатеки. MariaDB

12.04.2021 02:07:50 | Автор: admin

Лирическое отступление

Уже более года в качестве медиацентра использую KODI. Начиналось всё с каких-то базовых вещей, но даже от такого функционала я был в полном восторге. Просмотр ТВ, фильмов и сериалов теперь не выглядит каким-то квестом, а все мои домашние также полюбили KODI, и я даже не представляю, как раньше могли справляться без него.

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

Если вы пропустили предыдущие публикации обязательно загляните в них, возможно и для вас найдется что-то интересное. Если в двух словах установили и настроили с нуля ОС и KODI, настроили просмотр торрент-контента, YouTube, IPTV. Поговорили об управлении с других устройств, резервном копировании, анализе трафика и даже научили KODI запускать ретро-игры.

Все предыдущие публикации:

KODI: собираем удобный и функциональный медиацентр для дома. Часть 1
KODI: собираем удобный и функциональный медиацентр для дома. Часть 2
KODI: собираем удобный и функциональный медиацентр для дома. Часть 3. Ретро-игры
KODI: собираем удобный и функциональный медиацентр для дома. Часть 4. Архив IPTV
KODI: собираем удобный и функциональный медиацентр для дома. Часть 5. Яндекс.Музыка

Зачем это все затевалось?

Среди медиаустройств, в моем домашнем обиходе два телевизора. Основной в гостиной работает с KODI, второй на кухне под управлением Tizen OS. Мысль о том, что последнего также нужно посадить на KODI возникла спустя пару недель после запуска медиацентра на основном телевизоре. Но руки все никак не доходили

Каждый раз, переключая ужасного качества каналы кабельного ТВ на кухне, я вспоминал о своей затее. И вот пришло время воплотить ее в жизнь. Для этой задачи в поднебесной был заказан Raspberry Pi 3 и все та же аэромышь, как и для основного телевизора (подробнее о ней в первой части).

Итого, в гостиной KODI на базе неттопа с Kubuntu 20.04 на борту, на кухне LibreELEC на базе третьей малинки.

Кухонный медиацентр должен выполнять всего две задачи:
просмотр IPTV. Буду использовать все тот же сервис ilook и дополнение PVR IPTV Simple Client. К слову, сервис позволяет использовать плейлист на двух устройствах без дополнительной платы за тариф.
просмотр торрент-видео. Потому как локальной библиотеки фильмов и сериалов у меня нет.

О том, как установить LibreELEC на малинку описывать, думаю, не стоит. А как подключить IPTV и Elementum для просмотра торрентов я рассказывал в другой публикации.

Общее понимание архитектуры есть, причем же здесь MariaDB в заголовке? Представьте, вы смотрите фильм в гостиной, идете обедать на кухню и продолжаете просмотр ровно с того места, на котором остановились. Здорово, не правда ли?

KODI позволяет реализовать синхронизацию медиатеки на разных устройствах. Для этого потребуется MySQL-сервер, на котором и будет храниться эта медиатека. Сервер может быть поднят на совершенно сторонней машине, хоть под управлением Windows. В моем случае, основной медиацентр работает 24/7, аппаратные ресурсы позволяют сервером назначает его, на нем и будем поднимать базу данных.

Надеюсь, суть и прелести такой архитектуры домашних медиацентров понятна.

Подготовка серверной части. MariaDB

Прочитав Wiki на официальном сайте KODI, решил, что все довольно просто и понятно бери и делай. Но на практике оказалось все иначе БД не наполняется моей медиатекой, в логах ошибки не поддаются пониманию или записей нет вообще, от того понять, что сделано не так достаточно трудно. Второе устройство подключать к БД смысла нет синхронизировать нечего.

В комментариях к предыдущей публикации был затронут этот вопрос и @vyacheslavteplyakov дал понять, что Wiki не обновляется и информация, вероятно, устарела. Но и указал на основные подводные камни и как их обойти.

Особенности:

  • Использовать MariaDB;

  • Медиатека должна быть размещена в сетевом каталоге;

  • Все пути к медиатеке на всех устройствах должны быть абсолютные;

  • Сетевые каталоги NFS или SMB, если вынуждены использовать NTFS, то только с авторизацией по учетной записи с паролем;

  • Версии KODI на всех устройствах должны быть одинаковы.

С задачей и подводными камнями разобрались приступаем к работе. Напомню, сервером у нас будет KODI на неттопе с Kubuntu 20.04.

Устанавливаем сервер MariaDB

sudo apt updatesudo apt install mariadb-server

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

Запускаем скрипт безопасности

sudo mysql_secure_installation

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

root@kodi-pc:/# sudo mysql_secure_installationNOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!In order to log into MariaDB to secure it, we'll need the currentpassword for the root user.  If you've just installed MariaDB, andyou haven't set the root password yet, the password will be blank,so you should just press enter here.Enter current password for root (enter for none):

Пароль не задаем, нажимаем ENTER.

Setting the root password ensures that nobody can log into the MariaDBroot user without the proper authorisation.Set root password? [Y/n]

Отклоняем (N).

By default, a MariaDB installation has an anonymous user, allowing anyoneto log into MariaDB without having to have a user account created forthem.  This is intended only for testing, and to make the installationgo a bit smoother.  You should remove them before moving into aproduction environment.Remove anonymous users? [Y/n]

Удаляем (Y).

Normally, root should only be allowed to connect from 'localhost'.  Thisensures that someone cannot guess at the root password from the network.Disallow root login remotely? [Y/n]

Отклоняем (N).

By default, MariaDB comes with a database named 'test' that anyone canaccess.  This is also intended only for testing, and should be removedbefore moving into a production environment.Remove test database and access to it? [Y/n]

Удаляем (Y).

Reloading the privilege tables will ensure that all changes made so farwill take effect immediately.Reload privilege tables now? [Y/n]

Соглашаемся (Y).

Теперь создадим пользователя, из-под которого будут работать с БД наши медиацентры. Для создания пользователя kodi с паролем kodi запускаем оболочку MariaDB и выполняем команду

sudo mariadbGRANT ALL ON *.* TO 'kodi'@'localhost' IDENTIFIED BY 'kodi' WITH GRANT OPTION;

Разрешаем доступ с любого хоста ко всем базам данных на сервере для пользователя kodi

GRANT ALL PRIVILEGES ON *.* TO kodi@'%' IDENTIFIED BY 'kodi';

Очищаем привилегии, чтобы они были сохранены и доступны в текущем сеансе

FLUSH PRIVILEGES;

Оболочку MariaDB можно закрывать, выполнив команду

exit

Для организации доступа вне локального хоста, необходимо указать порт 3306 и bind-address 0.0.0.0. Открываем конфигурационный файл MariaDB

sudo mcedit /etc/mysql/mariadb.conf.d/50-server.cnf

Раскомментировать параметр

port = 3306

Для параметра bind-address установить 0.0.0.0 (вместо 127.0.0.1)

bind-address = 0.0.0.0

Для применения изменений перезапускаем MySQL-сервер

sudo service mysql restart

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

Как вариант - MySQL Workbench.

Создаем новое подключение:
Connection Method - Standart (TCP/IP)
Hostname 192.168.0.50 (заменить на адрес вашего сервера)
Port 3306
Username kodi (имя пользователя, если создавали своего)

Нажимаем Test Connection, вводим пароль и в случае, если все корректно получаем соответствующее сообщение:

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

Подготовка серверной части. KODI

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

Библиотеку мы настраивали еще в первой части. Никуда ее не переносим, пути оставляем прежние:
/mnt/kodi/library/Movies библиотека фильмов
/mnt/kodi/library/Shows библиотека сериалов

Необходимо лишь расшарить каталог /mnt/kodi/library. Конфигурируем samba

sudo mcedit /etc/samba/smb.conf

В конец конфигурационного файла вставляем:

[library]comment = librarypath = /mnt/kodi/library/browsable = yeswritable = yesguest ok = yesread only = noforce user = nobodyforce group = nogroupforce create mode = 0777force directory mode = 0777

И перезапускаем сервис samba

sudo /etc/init.d/smbd restart

Теперь наша библиотека будет доступна всем устройствам в домашней сети.

Важно! В источниках медиатеки также необходимо указать абсолютный путь к директории с файлами библиотеки! Хоть она и находится локально на этом устройстве (сервере).

Переходим в Настройки/Медиа/Медиатека/Видео и в существующих источниках Movies и Shows изменяем пути на нужные, выбрав Добавить сетевой адрес. В качестве логина и пароля используйте данные вашей учетной записи в Kubuntu.

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

Теперь дадим понять KODI, что мы хотим работать с базой данных. Для этого в файле advancedsettings.xml (/home/имя_пользователя/.kodi/userdata/) добавить:

<advancedsettings>  <videodatabase>    <type>mysql</type>    <host>192.168.0.50</host>    <port>3306</port>    <user>kodi</user>    <pass>kodi</pass>  </videodatabase>  <videolibrary>    <importwatchedstate>true</importwatchedstate>    <importresumepoint>true</importresumepoint>  </videolibrary></advancedsettings>

Если файл advancedsettings.xml отсутствует создайте его вручную. Параметры изменить в соответствии с вашими настройками, где:
Host IP-адрес вашего MySQL-сервера;
User имя пользователя MariaDB;
Pass пароль пользователя MariaDB.

На стороне сервера всё готово. Можно проверить. Перезапускаем KODI и, в зависимости от объема вашей медиатеки, ждем какое-то время, пока KODI ее обработает.

Информация о моей медиатеке:
Фильмов 322
Сериалов 68

Размер - 380 Кб

Общее количество файлов (nfo и strm) 3826

Моя медиатека обрабатывалась порядка 10 минут. По завершении обновления медиатеки давайте посмотрим на нашу БД. Снова подключаемся к серверу с помощью MySQL Workbench.

Как видим, KODI самостоятельно создал БД MyVideos119 и наполнил ее всеми нашими фильмами и сериалами. Например, в таблице Movie - фильмы. Значит, мы все сделали правильно.

После завершения импорта медиатеки в БД, можно еще оценить и ее ресурсопотребление. Моя медиатека заняла в ОЗУ чуть более 100 Мб. Это дает понять, что, даже значительный рост количества фильмов и сериалов, не скажется на производительности основного моего медиацентра.

Настройка клиентской части

Настройки других устройств, которые необходимо синхронизировать с сервером, очень просты:

  • advancedsettings настраиваем, также как на сервере;

  • добавляем источники в Настройки/Медиа/Медиатека/Видео также, как и на сервере, указывая абсолютные сетевые пути;

  • если вы, как и я используете дополнение Elementum в его настройках также изменить пути на абсолютные до сервера (путь к библиотеке и путь к торрент-файлам). Таким образом, где бы вы не начали смотреть (скачивать) фильм, сохранится он на сервере. Да и в моем случае, не рационально было бы для малинки иметь свое файловое хранилище.

Для упрощения настройки своей малинки я просто перенес конфигурационные файлы с основного медиацентра:

/home/kodi/.kodi/userdata/advancedsettings.xml
/home/kodi/.kodi/userdata/sources.xml
/home/kodi/.kodi/userdata/addon_data/plugin.video.elementum/settings.xml
/home/kodi/.kodi/userdata/addon_data/script.elementum.burst/settings.xml

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

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

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

Всем хорошего времяпрепровождения с медиацентром KODI!

Подробнее..

Бюджетный профессиональный свет для макияжа или для стрима себя любимого

13.04.2021 22:15:19 | Автор: admin

Есть несколько вариантов сделать качественное освещение быстро, дешево:

Вариант первый - галогеновые (галогенные) лампы

минусы: мало фиолетового/синего в спектре или очень маленький срок службы <100 часов (для ламп работающих с перекалом) и требуются хорошие софтбоксы для рассеивания света и защита их от воспламенения :)

Вариант второй - светодиоды

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

Все эти варианты решаемые, но вопрос цены и целесообразности.

Но есть еще один вариант - "дневные лампы"

Вы скажете "но это прошлый век..."

А вот и нет, нас интересуют только определенные лампы: Philips - серии "graphica" ЦТ 5000k - 2330лм (65лм/вт) и ЦТ 6500k - 2100лм (58лм/вт)

Упаковка максимально простая, явно не для продажи в масс-маркете :)

Некоторые путают с "обычными" дневными лампами с высоким индексом цветопередачи, выглядит они так:

Даташит graphica 950 и graphica 965, они отличаются цветовой температурой 5000k и 6500k, ниже спектры ламп с сайта производителя

graphica 950graphica 950graphica 965graphica 965

Некоторые путают данные лампы с "обычными" 9XX серии, вот спектр "обычной" 965

"обычная дневная лампа" 965"обычная дневная лампа" 965

Спектр светодиода "обыкновенного" 6500k, CRI>90, масштаб другой, но что было на сайте Philips

LED 965LED 965

Средняя стоимость лампы 1200-1500р, заявленный ресурс - 8000ч (на некоторых форумах встречал цифру в 5000ч) - после этого падает стабильность цветового охвата, а если точнее - начинает "плыть" дальний красный.

Т.к. лампа не за 100р, для нее требуется нормальный ЭПРА (с прогревом и плавным стартом). Если обходимся без "колхоза", то смотрим в сторону готовых ЛПО.

Philips vs Osram (оба 1x36), цена вопроса 1500-1800р

Мне нравятся оба, они имеют металлическое основание, но Philips имеет зазор между лампой и рассеивателем, что дает более мягкий свет.

Хотя ЛПО Philips больше похож на тот, который делался изначально под ЭмПРА, просто у него производитель провел небольшой upgrade :)

Подробнее..

Перевод Распознавание маски на лице с помощью YOLOv3

19.04.2021 16:09:25 | Автор: admin

YOLO или You Only Look Once это архитектура свёрточных нейронных сетей, которая используется для распознавания множественных объектов на изображении. В 2020, на фоне пандемии, задача детектирования объектов (object detection) на изображении стала как никогда актуальной. Эта статья даёт полное пошаговое руководство для тех, кто хочет научиться распознавать объекты с помощью YOLO на разных данных. Предполагается, что вы уже знаете, как делать распознавание объектов с помощью методов глубокого обучения и, в частности, вы знаете основы YOLO, так что давайте погрузимся в нашу задачу.


Я собираюсь работать с YOLOv3 одной из самых ходовых версий YOLO, которая включает в себя современную, удивительно точную и быструю систему обнаружения объектов в реальном времени. Новые версии, такие как YOLOv4, YOLOv5, могут достичь даже лучших результатов. Этот проект вы найдёте в моём репозитории на Github.

Рабочее окружение

Чтобы реализовать проект, я использовал Google Colab. Первые эксперименты с предварительной обработкой не были вычислительно дорогими, поэтому выполнялись на моём ноутбуке, но модель обучалась на GPU Google Colab.

Активировать GPU в Colab можно в меню Edit->Notebook Settings.Активировать GPU в Colab можно в меню Edit->Notebook Settings.

Набор данных

Для начала, чтобы сделать детектор маски, нужны соответствующие данные. Кроме того, из-за природы YOLO нужны аннотированные данные с ограничивающими прямоугольниками. Один из вариантов создать собственный набор данных, либо собирая изображения из Интернета, либо фотографируя друзей, знакомых и аннотируя фотографии вручную с помощью определённых программ, таких как LabelImg.Однако оба варианта утомительные и трудоёмкие, особенно последний. Есть другой вариант, самый жизнеспособный для моей цели, работать с публичным набором данных.

Я выбрал Набор обнаружения маски на лице от Kaggle и загрузил его прямо в мой Google Drive. Посмотрите здесь, как это можно сделать. Скачанный набор данных это две папки:

  • images, содержит 853 .png файла;

  • annotations, содержит 853 соответствующих аннотации в формате XML.

После загрузки набора данных, чтобы тренировать нашу модель, нам нужно преобразовать файлы .xml в .txt, а если точнее, в формат YOLO. Пример:

<annotation>    <folder>images</folder>    <filename>maksssksksss0.png</filename>    <size>        <width>512</width>        <height>366</height>        <depth>3</depth>    </size>    <segmented>0</segmented>    <object>        <name>without_mask</name>        <pose>Unspecified</pose>        <truncated>0</truncated>        <occluded>0</occluded>        <difficult>0</difficult>        <bndbox>            <xmin>79</xmin>            <ymin>105</ymin>            <xmax>109</xmax>            <ymax>142</ymax>        </bndbox>    </object>    <object>        <name>with_mask</name>        <pose>Unspecified</pose>        <truncated>0</truncated>        <occluded>0</occluded>        <difficult>0</difficult>        <bndbox>            <xmin>185</xmin>            <ymin>100</ymin>            <xmax>226</xmax>            <ymax>144</ymax>        </bndbox>    </object>    <object>        <name>without_mask</name>        <pose>Unspecified</pose>        <truncated>0</truncated>        <occluded>0</occluded>        <difficult>0</difficult>        <bndbox>            <xmin>325</xmin>            <ymin>90</ymin>            <xmax>360</xmax>            <ymax>141</ymax>        </bndbox>    </object></annotation>

Предположим, что это аннотация изображения, содержащего только 3 ограничительных прямоугольника, это видно по количеству <object> ... </object> в XML.

Чтобы создать подходящий текстовый файл, нам нужно 5 типов данных из каждого XML-файла. Для каждого <object> ... </object> в XML-файле извлеките класс, то есть поле <name>...</name> и координаты ограничивающего прямоугольника 4 атрибута в <bndbox>...</bndbox>. Подходящий формат выглядит так:

<class_name> <x_center> <y_center> <width> <height>

Я написал скрипт, который извлекает 5 атрибутов каждого объекта в каждом XML-файле и создаёт соответствующие файлы TXT.Подробные комментарии о подходе к преобразованию вы найдёте в моём скрипте. К примеру, у Image1.jpg должен быть соответствующий Image1.txt, вот такой:

1 0.18359375 0.337431693989071 0.05859375 0.101092896174863390 0.4013671875 0.3333333333333333 0.080078125 0.120218579234972671 0.6689453125 0.3155737704918033 0.068359375 0.13934426229508196

Это точное преобразование вышеупомянутого файла .xml в подходящий текст.

Примечание: очень важно сгруппировать изображения и соответствующие TXT в одну папку.

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

Пока всё хорошо, давайте продолжим.

Разделение данных

Чтобы обучить нашу модель и проверить её на этапе обучения, мы должны разделить данные на два набора набор обучения и набор тестирования. Пропорция составила 90 и 10 % соответственно. Поэтому я создал две новые папки и поместил 86 изображений с аннотациями в test_folder, а остальные 767 изображений в train_folder.

Клонирование фреймворка darknet

Следующий шаг клонировать репозиторий darknet с помощью команды:

!git clone https://github.com/AlexeyAB/darknet

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

!wget https://pjreddie.com/media/files/darknet53.conv.74

darknet53.conv.74 основа сети YOLOv3, которая вначале обучается классификации на наборе данных ImageNet и играет роль экстрактора. Чтобы использовать её для распознавания, дополнительные веса сети YOLOv3 перед обучением инициализируются случайным образом. Но, конечно, на этапе обучения сеть получит надлежащие веса.

Последний шаг

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

  1. face_mask.names: создайте содержащий классы задачи файл _.names.

В нашем случае исходный набор данных Kaggle имеет 3 категории: with_mask, without_mask и mask_weared_incorrect [с маской, без маски, маска надета неправильно].

Чтобы немного упростить задачу, я объединил две последние категории в одну. Итак, есть две категории, Good и Bad, на основании того, правильно ли кто-то носит свою маску:

1. Good.2. Bad.
  1. face_mask.data: создайте файл _.data, который содержит релевантную информацию о нашей задаче, с ним будет работать программа:

classes = 2train = data/train.txtvalid  = data/test.txtnames = data/face_mask.namesbackup = backup/

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

3. face_mask.cfg: Этот конфигурационный файл должен быть адаптирован к нашей задаче, а именно нам нужно скопировать yolov3.cfg переименовать его в _.cfg и изменить код, как описано ниже:

  • строку batch на batch=64;

  • строку subdivisions на subdivisions=16. В случае проблемы с памятью увеличьте это значение до 32 или 64;

  • входные размеры на стандартные: width=416, height=416;

  • строку max_batches на (#classes * 2000), это даст 4000 итераций для нашей задачи.

Я начал с разрешения 416x416 и обучил свою модель на 4000 итераций, но, чтобы достичь большей точности, увеличил разрешение и продлил обучение ещё на 3000 итераций. Если у вас есть только одна категория, вы не должны тренировать свою модель только до 2000 итераций. Предполагается, что 4000 итераций это минимум.

  • измените строку steps на 80% и 90% max_batches. В нашем случае 80/100 * 4000 = 3200, 90 / 100 * 4000 = 3600;

  • нажмите Ctrl+F и найдите слово "yolo". Поиск приведёт прямо к yolo_layers, где вы измените количество классов (в нашем случае classes=2) и количество фильтров. Переменная filters это вторая переменная выше строки [yolo].

    Строка должна стать такой: filters=(classes + 5) * 3, в нашем случае это filters = (2 + 5) * 3 = 21. В файле .cfg есть 3 слоя yolo_layers, поэтому упомянутые выше изменения нужно выполнить трижды.

4. Файлы train.txt и test.txt: Эти два файла были включены в файл face_mask.data и указывают абсолютный путь каждого изображения к модели. Например, фрагмент моего файла train.txt выглядит так:

/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss734.png/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss735.png/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss736.png/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss737.png/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss738.png...

Как я уже говорил, файлы .png должны располагаться в одной папке с соответствующими текстовыми аннотациями.

Это означает, что проект структурирован так:

MyDrivedarknet      ...      backup      ...      cfg            face_mask.cfg      ...      data            face_mask.data            face_mask.names            train.txt            test.txtface_mask_detection      annotations       (contains original .xml files)      images            (contains the original .png images)      mask_yolo_test    (contains .png % .txt files for testing)      mask_yolo_train   (contains .png % .txt files for training)       show_bb.py       xml_to_yolo.py

Начало обучения

После компиляции модели нужно изменить права на папку darknet, вот так:

!chmod +x ./darknet

И начинаем тренировать модель, запустив такую команду:

!./darknet detector train data/face_mask.data cfg/face_mask.cfg backup/face_mask_last.weights -dont_show -i 0 -map

Прописываем флаг -map, чтобы в консоль выводились важные показатели, такие как average Loss, Precision, Recall, AveragePrecision (AP), meanAveragePrecsion (mAP) и т. д.

Однако индикатор mAP в консоли считается лучшей метрикой, чем Loss, поэтому обучайте модель до тех пор, пока mAP возрастает.

В зависимости от различных параметров обучение может занимать часы, это нормально. Мне понадобилось около 15 часов, но первые впечатления от модели я получил примерно после 7 часов, то есть 4000 итераций.

Тестирование

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

!./darknet detector test data/face_mask.data cfg/face_mask.cfg backup/face_mask_best.weights

Вы заметили, что мы использовали face_mask_best.weights, а не face_mask_final.weights? К счастью, наша модель сохраняет лучшие веса (mAP достиг 87,16 %) в папке резервного копирования на случай, если мы тренируем её на большем количестве эпох, чем следовало бы (что, возможно, приведёт к переобучению).

Изображения ниже взяты из Pexels, набора изображений высокого разрешения, и невооружённым глазом видно, что они существенно отличаются от тестового и тренировочного наборов данных и, таким образом, имеют другое распределение. Чтобы посмотреть, насколько модель способна к обобщению, я выбрал эти фото:

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

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

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

Я заметил, что относительно стоящего впереди человека модель не столь уверена (38 % в чёткой области) в сравнении с прогнозом для человека сразу за ним (100 % в размытой области). Это может быть связано с качеством обучающего набора данных, таким образом, модель, по-видимому, в определённой степени подвержена влиянию (по крайней мере она не является неточной).

Один последний тест

Конечно, большое преимущество Yolo её скорость. Поэтому я хочу показать вам, как она работает с видео:

!./darknet detector demo data/face_mask.data cfg/face_mask.cfg backup/face_mask_best.weights -dont_show vid1.mp4 -i 0 -out_filename res1.avi

Оптимизировано для HabraStorage, gif с потерями.Оптимизировано для HabraStorage, gif с потерями.

Это был мой первый пошаговый туториал о том, как сделать собственный детектор с помощью YOLOv3 на пользовательском наборе данных. Надеюсь, он был вам полезен. А если хотите научиться создавать собственные нейронные сети и решать задачи с помощью глубокого обучения обратите внимание на курс Machine Learning и Deep Learning.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Текущее положение дел по распознаванию лиц и камерам наблюдений в Москве и мире

20.04.2021 22:19:53 | Автор: admin
image

Количество камер наблюдения в столицах и ключевых городах мира. Слева направо, сверху вниз: Москва 193 000, Лондон 627 707, Нью-Йорк 31 490, Пекин 1 150 000, Париж 26 834, Шэньчжэнь 400 000.

Как развивались события и росла (нейро)сеть камер наблюдения в Москве.

2016 год


Февраль 2016 года. NtechLab выпустила в открытый доступ FindFace: пользователи могли находить людей во ВКонтакте по фотографии на улице или в транспорте.

20 апреля 2016 года пользователи Двача деанонимизировали российских порноактрис с помощью FindFace.

7 июля 2016 года Даниил Туровский рассказал о приложении FindFace и технологиях тотальной слежки

2017


28 сентября 2017 года власти Москвы начали подключать к городской системе видеонаблюдения технологию распознавания лиц российской компании NTechLab. За два месяца работы системы в тестовом режиме с её помощью удалось задержать шестерых преступников, которые находились в федеральном розыске.

2018 год


FindFace Security от NtechLab позволила задержать более 180 правонарушителей на ЧМ-2018, часть задержанных находилась в федеральном розыске.

2019 год


27 июня 2019 года. МВД раскрыло результаты тестового внедрения систем распознавания лиц в метро и на улицах Москвы. С помощью 1000 камер снаружи подъездов жилых домов сотрудники правоохранительных органов задержали 90 человек.

В ноябре 2019 года мэрия заказала 258 серверов, 105 компьютеров и систему хранения данных общей емкостью 9 петабайт на 1,2 млрд руб.

2020 год


В январе 2020 года был объявлен конкурс на 1,9 млрд руб. на поставку оборудования для системы распознавания лиц на транспорте.

С 1 сентября 2020 года в московском метрополитене заработала система видеонаблюдения. Она охватывает все станции метро. Теперь планируется развивать эту систему в направлении пассажирских сервисов. Одним из них станет FacePay, который позволяет оплачивать проезд по скану лица.

15 ноября 2020 года Департамент правительства Москвы по конкурентной политике выделил 252,6 млн руб. для расширения системы хранения данных интеллектуальной системы видеонаблюдения (ИСВН) в метро. Соответствующий тендер опубликован на портале госзакупок. Подрядчик должен будет установить не менее 2220 жестких дисков объемом не менее 4 Тбайт. Общая вместимость хранилища записей с камер наблюдения увеличится минимум на 8880 Тбайт. (Источник)

Национальный институт стандартов и технологий США (NIST) провёл исследование и подтвердил, что системы не справляются с масками. Но уже спустя полгода компании решили эту проблему в исследовании NIST от декабря 2020 года все три компании из России попали в список тех, кто совершает меньше всего ошибок.

2021


2 марта 2021 года. Более 900 преступников задержали благодаря системе распознавания лиц в Московском метрополитене за полгода.

Мэрия Москвы до 2025 года потратит 2,917 млрд руб. на систему видеонаблюдения с функцией распознавания лиц в Новой Москве.

Затраты Москвы на модернизацию систем умного видеонаблюдения и анализа информации о гражданах постоянно растут. Если в 2019 году ДИТ потратил на эти цели 60,8 млрд руб., в 2020-м около 68 млрд руб., то по итогам 2021 года расходы ожидаются на уровне 70,8 млрд руб., следует из информации на сайте Мосгордумы.

image

Центр видеонаблюдения, куда стекаются данные ДИТ. Фото Антона Тушина для ТАСС

Мировая статистика по камерам слежения


Отчет Surfshark.

  • Ченнаи в Индии город с самой высокой плотностью камер видеонаблюдения: 657,28 на км2.
  • В Лондоне, Англия, больше всего камер видеонаблюдения на кв. км за пределами Азии: 399,27/ км2.
  • Нью-Йорк город США с самой высокой плотностью камер видеонаблюдения: 25,97/км2.
  • В Пекине в Китае больше всего камер (1,15 млн), но он занимает 10-е место по плотности (277,51/км2).
  • Шесть из 10 городов с наибольшей плотностью камер видеонаблюдения находятся в Китае, три Индии.


image


Топ-9 городов по количеству установленных камер. С 4 по 22 места Индия и Китай. Москва на 23 месте.

image


Топ-9 городов по концентрации на кв км. Париж 12 место, Багдад 16 место, Гвадалахара 17 место, Сингапур 23 место, Буэнос-Айрес 29 место, Москва 30 место, Санкт-Петербург 46 место

Не только камеры, не только лицо


image

2 февраля 2021 года. Министерство внутренних дел России намерено при помощи искусственного интеллекта определять преступников по голосу. Тендер на соответствующую программу и оборудование был размещен на официальном сайте.

А разработки MIT позволяют видеть сквозь стены:

image

Статья на Хабре: Машинное (радио)зрение видит сквозь стены



Выводы


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

Понятие приватности нужно оставить в XX веке. В XXI оно не применимо. Все будут знать про вас всё, это лишь вопрос времени. И вы от этого только выиграете.
Александр Минин, гендиректор NtechLab


Источники


Подробнее..

Перевод - recovery mode Как написать скрипт HelloWorld для Kodi на Python 2.x

01.05.2021 10:11:55 | Автор: admin

1. Введение


Напишите ваш первый скрипт для Kodi/XBMC с помощью этого урока! Если вам нужна помощь с написанием плагина, открывайте урок http://kodi.wiki/view/Audio-video_add-on_tutorial

2. Инструменты


Для вашего первого скрипта выбирайте текстовые редакторы notepad++ или sublime из-за их подсветки синтаксиса. Установите Kodi себе на компьютер.

URL = http://www.sublimetext.com
URL = http://notepad-plus-plus.org

3. Установка


Найдите git-репозиторий официального скрипта HelloWorld здесь https://github.com/zag2me/script.hello.world

и установите его в Kodi отсюда https://github.com/zag2me/script.hello.world/archive/master.zip с помощью zip-файла.

4. Первый запуск


Откройте ваше диалоговое окно с тремя строками текста с помощью команды
Система >> Дополнения >> Мои дополнения >> Программные дополнения >> Hello World.



5. В папке addons


Не закрывая Kodi, откройте папку с вашим скриптом и исправьте его! В Windows она находится здесь: C:\Users\user\AppData\Roaming\XBMC\addons\script.hello.world



Если у вас Mac, ищите ее тут: /Users/<your_user_name>/Library/ApplicationSupport/Kodi/addons/script.hello.world [1]

6. Структура


addon.py пишите здесь вашу программу на Python.

addon.xml опишите здесь ваш скрипт.

changelog.txt обновляйте этот текстовый файл с каждой новой версией.

icon.png нарисуйте знак вашего скрипта в квадрате со стороной 256 или 512 пикселей.

LICENSE.txt расскажите об авторских правах на ваш скрипт.

7. XML


Прежде всего укажите ID, название и версию вашего скрипта, а также имя его автора с помощью первой строки XML-файла. Придумайте уникальный ID, как на скриншоте ниже.

Объясните XBMC с помощью секции Requires, какие модули вы ходите импортировать. Упомяните там скрипт xbmc.python.

Сообщите XBMC тип вашего скрипта с помощью extension point. Поместите туда executable с помощью элемента <provides>.

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



8. Программа


Назовем ее addon.py. Кстати, хотите изучить Python открывайте веб-сайт https://www.codecademy.com/learn/learn-python.

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

Далее идут строки для информирования системы о нашем скрипте и о его имени.

В строках 7, 8 и 9 начинается настоящая магия. С их помощью мы назначим значения трем переменным (line1, line2 и line3).

С помощью крайней строки покажем окно со значениями этих переменных на экране компьютера. Вместе с ними отобразим имя скрипта и сделаем кнопку ОК.



9. Другой код


Умея запускать ваш скрипт, понимая его структуру и зная, что он делает, изменим его!

Откройте папку с вашим скриптом, исправьте код. Будьте готовы запустить его с помощью Kodi, когда сделаете это. Для этого переключитесь между текстовым редактором и Kodi.

Поменяйте значения переменных.
line1 = Goodbye world!

line2 = And welcome back

line3 = My own Add-On!

Сохраните файл и запустите скрипт из Kodi. Вы увидите новые строки.

Поздравляем! Теперь вы XBMC/Kodi-программист!

10. Заключительные соображения


Очевидно, что с помощью интерфейса Python вы получите доступ к большему числу команд Kodi, чем узнали из этого урока. Также для этого имеется интерфейс JSON.

Не стесняйтесь обсуждать этот урок на ветке форума http://forum.kodi.tv/showthread.php?tid=209948, если у вас есть какие-либо вопросы.

11. Кое-что еще


Вы можете найти другие варианты скрипта HelloWorld с помощью гиперссылки http://kodi.wiki/view/GUI_Tutorial.

12. Примечания


1. Userdata https://kodi.wiki/view/Userdata
Подробнее..

Монтируем видео на облачном сервере в AWS

01.05.2021 20:09:31 | Автор: admin

Мой рабочий компьютер - Macbook Air 2020 (Intel), и его вполне хватает для написания кода и прочих задач. Однако, когда потребовалось смонтировать небольшой видеоролик, выяснилось что мощи моего ноута катастрофически не хватает, и я стал искать варианты.

Что я пытался сделать

Я не занимаюсь профессиональным монтажом видео, и впервые уткнулся в эту тему пытаясь сделать вот такой небольшой демонстрационный ролик для Teamplify (с озвучкой помог Мэтт, наш учитель английского):

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

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

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

  3. Нельзя просто так взять и записать качественный звук в домашних условиях.

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

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

Монтаж видео требует ресурсов

Мои попытки работать с видео на Macbook Air были похожи на мучение. Оказалось, что After Effects любит побольше памяти, и моих 16ГБ явно не хватало. Ноутбучный проц и слабая видюха также не улучшали картину. Превью в After Effects работало с сильными лагами. Финальный рендер трехминутного ролика занимал 3 часа (!!).

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

Что нам может предложить AWS?

Мне он предложил примерно следующее:

  • Регион eu-north-1 (Стокгольм), пинг до меня ~30мс (я живу в Вильнюсе), до Москвы чуть побольше, но тоже неплохо - около 40мс;

  • Сервер g4dn.2xlarge - 8 ядер, 32ГБ, Nvidia Tesla T4, 225 GB NVMe SSD;

  • Образ винды с дровами от Nvidia;

  • Цену в $1.166 в час. И это кстати еще одна причина выбрать Стокгольм, ибо во Франкфурте на ~12% дороже.

Разумеется, можно взять и побольше, и поменьше. На момент написания статьи цены в Стокгольме такие (инстансы с Nvidia Tesla T4, на винде):

Подготовка к работе

Для коннекта к серверу я использовал Microsoft Remote Desktop. Сначала я думал, что мне потребуются какие-то специальные клиенты, рассчитанные на стриминг видео, но оказалось что и обычный Remote Desktop неплохо справляется, никаких особенных лагов я не замечал.

Остается решить вопрос с диском и доставкой исходников на него. Тут амазона предлагает нам два варианта - EBS и Instance Store. Отличаются они следующим:

  • EBS - подключаемый сетевой диск, по надежности примерно как RAID 1. Такой диск в вашем сервере обязательно будет как основной, по умолчанию он небольшого размера (достаточного чтобы влезла операционка), но вы можете при желании его расширить. Данные на EBS сохраняются и после выключения сервера. Платите вы за такой диск постоянно, даже если сервер выключен, в размере $0.0836 за ГБ в месяц (в версии gp3, burstable IOPS). У EBS есть разные варианты по производительности. Можно купить гарантированную производительность, но тогда она может влететь в копеечку, или же можно остаться на стандартном gp3, который основан на модели burstable IOPS. Когда диск простаивает, у вас накапливаются кредиты на IO, а когда он активно используется - они расходуются. Подробнее можно почитать в доках.

  • Instance store - так называемый эфемерный диск, который может идти в комплекте к некоторым типам инстансов. В частности, к g4dn.2xlarge прилагается 225 GB NVMe SSD. Он быстрый, производительность ничем не ограничена, однако данные на нем полностью пропадают при выключении сервера (при перезагрузке - остаются).

Поскольку монтаж видео активно использует и диск тоже, использование Instance store выглядело для меня более привлекательным. По умолчанию он не монтируется как диск в Windows, поэтому нужно сделать небольшую настройку чтобы он автоматически подключался при запуске.

Когда диск готов, нужно как-то доставить на него исходники. Я использую для этого pCloud (аналог Dropbox), который установлен и на моем ноуте, и на облачном сервере. При старте сервера на нем запускается небольшой батничек, который инициализирует расшаренную папку pCloud (для исходников и результатов) и заодно создает папку для кеша After Effects:

mkdir Z:\"Teamplify Videos"mkdir Z:\"AE Cache"

После этого стартует pCloud, который синхронизирует папку Teamplify Videos с исходниками. Отрендеренный результат я пишу в ту же папку, и таким образом он попадает обратно на мой компьютер.

Как возможную альтернативу Dropbox / pCloud можно еще рассмотреть что-то вроде ownCloud с хранилищем на S3. Если исходники будут выкачиваться из S3 в том же AWS регионе, что и ваш сервер, то это должно быть очень быстро, что может иметь значение при большом размере исходников.

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

И как это все работает?

Да вроде неплохо. Превью в After Effects нормальное, без лагов. Поскольку пинг до сервера хороший, работа на нем почти не отличается от работы с настольным компьютером. Рендер того ролика, который у меня на ноуте выполнялся три часа, на g4dn.2xlarge делается за 13-17 минут. День работы на таком сервере обходится в несколько долларов.

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

Подробнее..

Перевод - recovery mode Как написать видео плагин для Kodi

08.05.2021 10:22:37 | Автор: admin

1. Введение


Узнайте, как написать ваше первое видео расширение для Kodi/XBMC из этого урока!

2. Инструменты


Если вы делали урок про то, как написать скрипт HelloWorld, то у вас уже, наверное, установлен текстовый редактор, который нам понадобится и в этом уроке. Может быть, стоит установить еще и видео плеер, раз мы теперь имеем дело с видео. Вдобавок к Kodi установите vlc, такой же замечательный и открытый.

VLC www.videolan.org/vlc

3. Установка


В этом уроке нам понадобятся два достаточно простых видео плагина. Найдите их в git-репозитории.

Можете загрузить себе на компьютер оба zip-файла и установить видеодополнения из zip-файла внутри интерфейса Kodi. Также можете распаковать их в папку userdata/addons.

4. Первый запуск


Выполните команду Система >> Дополнения >> Мои дополнения >> Видео дополнения >> Example Kodi video Plugin и вы увидите несколько тестовых видео с некоего интернет-сервера.



5. Что это было


Видео плагин сделал свое дело. Как?

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

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

6. Структура


main.py вот она, ваша программа на Python.

addon.xml а это ее описание в формате xml.

icon.png значок для вашего плагина. Нарисуйте его в квадрате величиной 256 или 512 пикселей, сделайте это красиво.

Readme.md прочтите это на странице используемого в этом уроке видеодополнения на GitHub, чтобы знать, что и как в общем оно делает, а также откуда его можно скачать.

7. Текст программы


Скоро вы увидите текст видеодополнения. Код на Python, который составляет большую его часть, хранимый в простом текстовом файле.

Дадим Kodi понять, что нам нужно.



Сделаем еще кое-что



Чтобы не заморачиваться с парсингом или API ради ссылок на видеофайлы/потоки с какого-либо веб-сайта или онлайн-ресурса, как это делается в настоящих расширениях Kodi (потому что совсем не за тем был написан этот плагин), берем фиксированный набор свойств.



А теперь объявим это



Сообщим Kodi еще кое о чем



Без списка категорий никуда.



Расскажем Kodi, как мы перечислим наши видео



и где они находятся.



Добавим маршрутизацию в наш плагин



и передадим это Kodi.



8. Изменим код!


Умея запускать ваше расширение, понимая его структуру и зная, что оно делает, изменим его!

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

Откройте main.py из вашей папки userdata и как следует его исправьте.



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

Сохраните файл с помощью вашего текстового редактора и запустите видеодополнение из Kodi. Обратите внимание, как изменились видео в видеодополнении и его структура.

Поздравляем с тем, что ваше видеорасширение теперь воспроизводит новые видео из интернета!

9. Потоковое видео


Как опытный программист, я вам сейчас расскажу кое-что про настоящие видеодополнения.

Во-первых, я бы им не стал без документации на codedocs.xyz, которая поможет и вам не хуже какой-нибудь вики-энциклопедии.

codedocs.xyz/xbmc/xbmc

Кстати о документации. Руководство по ListItem будет вам все время помогать, если вы свяжетесь с потоковым видео.

codedocs.xyz/xbmc/xbmc/group__python__xbmcgui__listitem.html#gac31a08def90f50295146753353cb9541

Сами по себе вы мало что сделаете, полагаясь на стриминговую платформу, которая вам и передаст данные для вывода в Kodi элементов с URL-адресами видео. Когда вашим видео понадобится DRM, полагайтесь на InputStream.Adaptive.

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

получать необходимую информацию из интернета (пусть для начала это будут заголовки и url-адреса);

создавать элементы, которые Kodi будет рассматривать как источники видео (см. об этом xbmcplugin.addDirectoryItem() and xbmcgui.ListItem()).

Знакомство с написанием плагинов для работы с потоковым видео будет неполным без чтения исходников этих самых расширений, таких как AsciiDisco для Nexflix или Sandmann79 для Amazon/AmazonVOD.

Хотите прочитать об этом больше? Идите на ветку форума.

10. Заключительные соображения


На самом деле, написание видеодополнений дело довольно простое, как только вы получите хорошую базовую структуру. Можете парсить с помощью Regex многие онлайн-источники, либо размещать там свои собственные видео. С помощью простых в использовании дополнительных модулей у вас в Kodi появятся видеоклипы с youtube или vevo-видео.

Не стесняйтесь обсуждать этот урок на ветке форума, если у вас есть какие-либо вопросы.

11. Кое-что еще


Никогда не вводите табуляцию вместо пробелов в Python. Не спрашивайте почему. Просто запомните это.
Подробнее..

Motion Amplification или диагностика состояния промышленного оборудования и сооружений с помощью видеоаналитики

10.05.2021 12:10:00 | Автор: admin

Motion Amplification (англ.) усиление движения.

Прямых доказательств, связывающих рождение технологии Motion Amplification с силовыми ведомствами США, у нас нет, но косвенных достаточно. Не случайно среди примеров использования есть немало кейсов из аэрокосмической и оборонной отраслей. Измерение уровня вибрации вертолета во время полета важная, но очень непростая задача. С Motion Amplification она решается довольно быстро и просто

Диагностика состояния вертолета во время полета - YouTube

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

История

В конце 2014 года изобретатель Jeff Hay (основатель компании RDI Technologies) получил два патента под названием Бесконтактный мониторинг состояния мостов и гражданских сооружений и Аппарат и метод визуализации периодических движений механических компонентов. Пытливые умы могут почитать подробные материалы по ссылке, а для остальных кратко расскажем в чем заключается основная идея изобретения.

Если взять дорогую высокочастотную камеру и записать видео (например, тысячу кадров в секунду), то даже за 5 секунд мы получим внушительный массив данных. При движении объекта в объективе камеры происходят изменения пикселей изображений во время записи. Зная частоту съемки (fps), фокусное расстояние объектива и расстояние от камеры до объекта, можно выполнить точные измерения амплитуды движения. Фиксируя даже незначительное движение объекта в кадре, проприетарные алгоритмы при обработке видео усиливают это движение, делая его заметным каждому.

Воплощение идеи потребовало серьезных научных и практических изысканий, и первое решение под названием IRIS M появилось на рынке только в сентябре 2016 года. Функционал программного обеспечения версии 1.0 был прост запись видео и усиление движения. Только в декабре 2016 года (v.1.1) появилась возможность делать измерения. Несмотря на ограниченный функционал, экспертное сообщество и крупные корпоративные заказчики в США встретили новинку с большим энтузиазмом. Решение получило признание American Society of Civil Engineers в 2016г., а также было отмечено вторым призом престижного конкурса Vision Systems Design в 2017г.

Секрет успеха

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

Давайте сравним процесс оценки состояния обычного насоса традиционным способом и с помощью IRIS M. Сбор данных традиционным способом осуществляется обычно с помощью виброанализатора по ограниченному набору точек контроля.

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

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

Motion Amplification (МА) сочетает в себе преимущества традиционной вибродиагностики, фазового анализа и использования специализированного программного обеспечения ODS (Operational Deflection Shape) для анимации:

  • можно собрать в тысячи раз больше данных, чем традиционным способом;

  • все данные собираются одновременно, а не последовательно

  • данные для анализа фаз собираются за одну съемку, а не отдельно (как при традиционном подходе)

  • каждая точка изображения может быть использована для измерения уровня вибрации

  • обработанное видео является наглядным отчетом, понятным людям даже без специализированных знаний и опыта.

На видео для наглядности показаны обычная и обработанная видеозаписи. Давайте посмотрим на видеоотчет о диагностике состояния насоса с помощью МА

Диагностика насоса - YouTube

Решение IRIS M не только позволило увидеть, то что кажется невидимым человеку, но и помогло определить первопричину проблем с оборудованием трещину в раме. Короткое и наглядное видео выглядит гораздо убедительнее традиционных бумажных отчетов.

Чувствительность системы 0,25 микрона с расстояния 1 метр при использовании объектива с фокусным расстоянием 50 мм. Пара человеческих глаз не выдерживает конкуренцию.

Варианты решений

Помимо первого решения IRIS M, которое закрывает основные потребности заказчиков, на рынок были выпущены решение IRIS MX в 2018г. и IRIS CM в 2019г.

IRIS M делает 120 кадров в секунду в HD-разрешении и до 1300 в сокращенном, что позволяет уверенно диагностировать проблемы в частотном диапазоне от 0 до 520 Гц.

IRIS MX расширяет возможности базового решения и позволяет работать и в более высокочастотной области до 11600 Гц (1400 fps при HD-разрешении и 29000 fps при сокращенном разрешении), что позволило успешно диагностировать турбомашины.

Решение IRIS CM (continuous monitoring) хорошо подходит для мониторинга состояния активов на удаленных объектах, на которых нет специалистов по вибродиагностике. Несколько видеокамер можно объединить в сеть, чтобы получать видеоданные с разных ракурсов. Пользователи могут инициировать запись видео и данных на основе внешних триггеров (например, данных с акселерометров) при достижения пороговых значений вибрации.

Даташиты решений можно найти по ссылке РЕШЕНИЯ VIMS (motionamplification.ru)

Интерфейсы

Пакет приложений решений RDI, установленных на ноутбуке, содержит 4 программы. По ссылкам можно увидеть их интерфейсы:

1. Motion Explorer хранение и менеджмент файлов

Управление контентом Motion Explorer - YouTube

2.RDI Acquisition запись видео

Программа для записи видео RDI Acquisition - YouTube

3. MotionAmplification аналитика и измерения

Аналитическое приложение MotionAmplification - YouTube

4. Motion Studio редактор видео

Редактор видео Motion Studio - YouTube

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

Возможности решения

Подробную информацию о функционале программного обеспечения и его развитии в 2016-2020 гг. можно найти по ссылке

Обзор программного обеспечения MotionAmplification v1.0-3.0 - YouTube

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

Диагностика состояния огромных конструкций - YouTube

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

Диагностика резервуаров - YouTube

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

(1) Диагностика мачты буровой установки - YouTube

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

Вибрация трубопровода на НПЗ - YouTube

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

Диагностика состояния прокатного стана - YouTube

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

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

Диагностика виброгрохота и насоса. Частотная фильтрация видео. - YouTube

Функциональные возможности решения быстро расширялись последние годы. Так в версии 3.0 программного обеспечения в 2020 году появились:

  • усиление движения в режиме реального времени (Live MA), которое отлично подходит для быстрого сканирования состояния активов

  • векторы движения

  • тепловая карта движения (по частотам)

  • измерения движущихся объектов

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

Заключение

Если все так хорошо, то почему эти решения не используются на каждом крупном российском предприятии? Причин тут видится несколько:

  1. запрет в США на продажу решений двойного назначения в Россию, под который до 2021г. попадали и решения Motion Amplification

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

  3. длительные циклы принятия решений и выделения бюджетов в корпорациях

  4. в России по-прежнему доминирует планово-предупредительное обслуживание оборудования, диагностику на производствах во многих отраслях недооценивают и недофинансируют.

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

Мне представляется, что спрос на квалифицированных экспертов в узких предметных областях, таких как диагностика, будет по-прежнему высоким в ближайшие годы. Экспертно-ориентированные решения Motion Amplification существенно расширяют возможности по диагностике промышленных активов. Спектр применения и количество отраслей, использующих решения семейства IRIS, постоянно растут. С помощью этих решений процессы диагностики, технического обслуживания и ремонты на предприятиях становятся более эффективными и прозрачными.

Подробнее..

Камера против эвакуаторов, угонщиков, дачных гостей

17.05.2021 22:18:14 | Автор: admin


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

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

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

Алгоритм работы следующий. Камера после включения поднимает соединение через usb 3G модем, соединяется с VPN, мониторит картинку и, при появлении движения на ней, делает фото и, заодно, отправляет фото в телеграм-канал владельца. Из опций возможность записывать timelapse, видео и возможность покрутить головой удаленно, то есть поуправлять камерой и посмотреть что к чему. Нейросетей здесь нет, все достаточно просто.

Будет ли это работать?


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

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


оригинальная ссылка изображения

В качестве софта используется RPi-web-interface.

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

Отличия, дополнения следующие:

  • связь с telegram-каналом (отправка фото на канал),
  • 3g связь одновременно с наличием wi-fi,
  • обратная связь с камерой через vpn,
  • иные изменения в настройках.

Немного скучной предыстории


При разработке прототипа остро встали несколько вопросов:

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

Первая реализация камеры в части софта имела изобилие костылей.

Изначально все было собрано на raspberry pi 3b с неродной камерой (usb, не csi). В качестве обратной связи использовался skype. Для связи с камерой на нее надо было позвонить.
Через skype. В skype была функция автоответа, после снятия трубки устанавливался канал видеосвязи и можно было посмотреть то, что видела камера. Все просто.

И все просто жутко работало в плане скорости, звук был отвратительным и так далее. Потом skype перестала обновлять пакеты под raspberry. Была попытка продлить жизнь данного изделия с помощью exagear desktop, который эмулировал win и позволял установить такие приложения как icq, viber, teamspeak. Но все это работало крайне медленно и неэффективно. Потом exagear desktop, который был к тому же с платной лицензией (слава богу, единоразовый платеж) свернули и поддержку прекратили.



В плане питания все тоже было не совсем просто. Raspberry pi 3b любила и любит покушать, и powerbankи нужны были с достатком. Были попытки запитать от солнечный панелей, но сразу стало понятно, что этот вариант не подойдет.

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

Основа видеокамеры




В качестве базы, от который оттолкнемся при создании камеры будет та же raspberry pi, только модель zero w (c поддержкой wifi).

Наши комплектующие следующие:

  • raspberry pi zero w 1,8 к руб.;
  • камера со шлейфом 500 руб.
  • сервоприводы sg-90 2шт. 200 руб.
  • пластиковый подвес 100 руб.
  • блок питания 1000 руб.;
  • sd карта на 8, а лучше на 16Гб 1000 руб.;
  • 3g modem Huawei E171 2k руб.
  • sim карта с пакетом услуг интернет, например от мтс 500 руб.
  • переходник с mini-usb на usb для подключения usb 3g модема 200 руб.
  • коробка, в которой прислали raspberry pi-бесплатно.

Итого: 7300 руб.
Cхема соединения с сервами подвеса камеры та же, что использовалась ранее

Переходим к настройкам


Не будем далеко отходить от образа (image) с предустановленным Rpi-web-interface, servoblaster, приведенном в ранее опубликованной статье ссылка на образ. Его необходимо будет немного дополнить.

Первое, что настроим это 3g модем



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

Но в статье много лишних этапов, чтобы, так сказать, прочувствовать модем. Поэтому можно смело начинать настраивать с шага РЕКОННЕКТ ПРИ ДРОПЕ и не выполнять все предыдущие:

$ mkdir ~/3g$ cd ~/3g$ wget http://zool33.uni-graz.at/petz/umtskeeper/src/umtskeeper.tar.gz$ tar -xzvf umtskeeper.tar.gz$ chmod +x umtskeeper$ ./umtskeeper --sakisoperators "USBINTERFACE='0' OTHER='USBMODEM' USBMODEM='12d1:1506' APN='CUSTOM_APN' CUSTOM_APN='internet.mts.ru' APN_USER='mts' APN_PASS='mts'" --sakisswitches "--sudo --console" --devicename 'Huawei' --log --nat 'no'$ sudo nano /etc/rc.local/home/pi/3g/umtskeeper --sakisoperators "USBINTERFACE='0' OTHER='USBMODEM' USBMODEM='12d1:1506' APN='CUSTOM_APN' CUSTOM_APN='internet.mts.ru' APN_USER='mts' APN_PASS='mts'" --sakisswitches "--sudo --console" --devicename 'Huawei' --log --silent --nat 'no' &

*12d1:1506 изменить на свой, который можно узнать через lsusb.
**если модем не стартует, добавить sudo в начале команды.

После настройки модема и редактирования /etc/rc.local будет небольшой баг, так сказать. Несмотря на то, что rc.local запускает при загрузке все указанные в нем скрипты, umtskeeper запускается весьма неспешно. И на старт модема может уйти от 1 минуты до 3. Это может быть критично в некоторых ситуациях. Как с этим бороться, пока не ясно.

Теперь настроим отправку сообщений в telegram канал c raspberry pi



Нам понадобится следующая статья.

Здесь могут возникнуть сложности с настройкой botа в telegram, если нет опыта его создания через Godfather, а также получения токена и API, которые надо отразить в скрипте:

#Telegram detailstokenurl="http://personeltest.ru/aways/api.telegram.org/bot<your API key>"chatid="<your chat id>"

Однако, после настройки, функционал будет работать как часы:

картинка


Настройка параметров камеры



По умолчанию в Rpi-web-interface камера при включении мониторинга движения сначала делает снимок, а потом начинает писать потоковое видео. Об этом написано здесь. Вопрос Is it possible to record still images using the RPi Cam Web interface on motion detect?

С одной стороны это удобно, сделала пару фото, оправила в telegram и дальше пишет видео. Но на практике это приводит к подвисанию, так как запись идет пока есть движение в кадре, а в телеграм попадают только первые пару снимков.

Поэтому настройки камеры надо поменять следующим образом:





То вместо ca 1 и ca 0 написать только im in.
Теперь в telegram будут прилетать практически все фото.

Настройка VPN


ссылка на оригинал картинки

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

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

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

Третий (на нем было решено остановиться) услуга vpn, в частности vpnki.

На сайте предлагается бесплатный 14-дневный период, далее vpn-туннели блокируются, если не приобрести платный пакет услуг:



Сами тарифы не кусаются, а подробные инструкции помогут пройти несложный путь настройки туннелей. Их понадобится минимум два один для raspberry pi, второй для устройства, с которого будет доступ (допустим, телефон на android). В нашем случае для raspberry pi использовался этот вариант настройки.

И вот тут ждет еще один неприятный подводный камень, связанный опять же с rc.local.

Если коротко то модем и vpn конкурируют за соединение ppp0 и в итоге его занимает тот, кто быстрее запустится. Более подробно вопрос описан здесь.

Выход не запускать vpn пока не запустится модем (не установит соединение ppp). Далее вручную поднять vpn через команду

sudo pon vpnki updetach

Опять же здесь не решатся вопрос с падением vpn. *Свое соединение модем (umtskeeper) при падении восстанавливает и сообщения в телеграм приходить будут в любом случае, а вот vpn не оживает. Так что опция покрутить головой может немного хромать при плохой связи со стороны raspberry pi.

На этом все. Есть еще мысли добавить на web-интерфейс сведения о балансе mts и vpn.

Вопросы и предложения приветствуются.
Подробнее..

Как мы спасали видео одной вебкам-модели нейросетями

24.05.2021 12:21:25 | Автор: admin

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

Но мы решили пойти новым путем и прогнали звуковую дорожку через нейросети плагина Nvidia RTX voice, всего за 6 минут мы получили настолько впечатляющий результат, что мы не смогли бы спокойно спать, пока не:

  • разобрались, как работает эта нейросеть
  • проверили, как плагин работает на ASMR теряется ли эффект или становится лучшие


Что за видео мы чистили


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

У Ани довольно пикантная работа в 18+ стримингах и она решила объединить два хобби: во время стримов настраивать серверы по подсказкам зрителей.

Мы познакомились с Nvidia RTX voice, когда помогали ей чистить звук на первом ролике, где она поднимает VPN.

Но теперь давайте вернемся к нейросетям и ASMR может ли нейросетка уничтожить тот самый эффект?

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

Что такое ASMR и откуда берутся мурашки?


Несколько лет назад был открыт забавный эффект, который, у некоторых людей, вызывает очень своеобразные ощущения, при прослушивании записей, где намеренно преувеличены шуршащие и шипящие звуки. Его назвали: ASMR (Autonomous sensory meridian response или Автономная сенсорная меридиональная реакция). Сложное определение не облегчает понимание его природы, которое выражается в мурашках возникающих где-то в затылке и спускающихся по шее к спине. Ощущения, чаще всего, приятные и очень залипательные, больше всего они похожи на мурашки, которые вызывает массажер мозга с пружинками.


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

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


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


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


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

Как звукорежиссеры борятся с шепотом и шумом


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

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


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

Больше всего энергии человеческого голоса (около 60%) лежит в диапазоне от 63 Гц до 500 Гц, но информационное наполнение этого интервала всего 5%. Больше всего информации несет диапазон от 500 Гц до 1 кГц, ее в нем примерно 35%. Оставшиеся 60% информации приходится на шипящие звуки в диапазоне от 1 кГц до 8 кГц, но они несут всего всего 5% энергии. Хотя такие звуки самые слабые, от них сильно зависит разборчивость человеческой речи. Для удаления их из записи используется эквалайзер, при его правильной настройке, речь становится более четкой.


Хотя, микшерный пульт с огромным количеством настроек в разных частотных диапазонах, все еще является одним из главных атрибутов звукозаписывающей студии, в обработке используется и более современная технология: цифровой сигнальный процессор (digital signal processor, DSP). Комплекс, состоящий из узкоспециализированного микропроцессора и программ, который занимается автоматической обработкой аудиосигнала, сочетая в себе множество функций, которые раньше выполнялись с помощью ручной настройки или электронных схем. Помимо продвинутого эквалайзера, процессор может имитировать задержку сигнала для построения сцены в определенном участке помещения и многие другие функции обработки звука. DSP используются как в музыкальных плеерах, так и в системах распознавания речи, выделяя из сигнала наиболее информативные диапазоны частот.

Нейросети на тензорных ядрах: Nvidia RTX



Технология не стоит на месте, на смену ручному микшерному пульту и ограниченному набору обычных программ пришли нейронные сети. В 2017 году разработчики Nvidia опубликовали статью Interactive Reconstruction of Monte Carlo Image Sequences using a Recurrent Denoising Autoencoder, в которой описали метод машинного обучения для восстановления последовательностей изображений, полученных с помощью методов Монте-Карло, на его основе была создана программа NVIDIA OptiX AI-Accelerated Denoiser, которая занимается удалением шума с 3D-рендеров.

Если кто-то подзабыл суть метода Монте-Карло, мы напоминаем его в этой ASMR-записи:


На этом исследования не остановились: весной прошлого года, компания Nvidia представила продукт, который использует мощь современных видеокарт не только для взлома хэшей, майнинга крипты или банальных видеоигр, но и для улучшения качества видео-стриминга. Кроме вычитания фона из видеотрансляции, числомолотилка RTX используется в программе NVIDIA RTX Voice для интеллектуального шумоподавления в режиме реального времени. Теперь, владельцы дорогущих видеокарт могут значительно улучшить качество своих стримов.

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

Началось все с того, что Nvidia разработала графические процессоры содержащие тензорные ядра. Раньше в графике использовались процессоры с шейдерными ядрами, они отличаются от обычных настольных процессоров тем, что могут производить более простые вычислительные операции, но зато во множестве параллельных конвейеров. Узкая специализация позволила сильно увеличить скорость обработки графики и такие процессоры стали называться GPU.

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

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

В конце 2018 года, фирма NVIDIA представила новую возможность в своих видеокартах улучшение качества изображения с помощью нейросети под названием DLSS (Deep Learning Super Sampling), суперсемплинг с помощью глубокого обучения. Традиционный ресемплинг и сглаживание требуют огромного количества вычислений и являются причиной высокой стоимости видеокарт, увеличение разрешения изображения происходит с помощью тупых числомолотилок, которые, по сути своей, производят предельно простые математические операции, но в огромном количестве. DLSS работает по другому, чтобы избежать масштабных вычислений, картинка считается для сравнительно небольшого разрешения, а зачем растягивается с помощью искусственного интеллекта. Это требует намного меньше ресурсов, чем прямой пересчет, при сравнимом качестве изображения и позволяет получать хорошую картинку на не очень дорогих видеокартах, а на мощных существенно повысить частоту кадров.

Первые версии нейросети DLSS работали используя тензорные ядра процессоров RTX Turing. Но получилось совсем не так, как в радужных обещаниях разработчиков NVIDIA, картинка выглядела мутной и с большим количеством артефактов, будто нарисованная грубыми мазками масляных красок:


Картинка кликабельная при клике она откроется в более высоком разрешении

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

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

Отличия разглядеть можно, но они не бросаются в глаза, как в первой версии, а затраты по мощности заметно меньше, что позволяет увеличить частоту кадров до комфортной. Если первая версия алгоритма, работавшая на тензорных ядрах, не давала существенного прироста качества, по сравнению с рендерингом в 1440 и апскейлом до 4К, но еще и работала почти с той-же скоростью. То последняя версия, на шейдерных ядрах, работает быстрее апскейла и со сравнимым качеством.

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

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

Как NVIDIA RTX Voice автоматически подрезает шум



После успешного запуска DLSS для графики, разработчики Nvidia не остановились на достигнутом и расширили примирение нейросети до обработки звука, плагин NVIDIA RTX Voice построен на ее основе, но алгоритм немного отличается. Нейросеть используемая в этом случае, относится к вариационным автоэнкодерам или автокодировщикам.


В отличии от традиционных нейросетей, у которых в промежуточных слоях очень много нейронов, у автоэнкодера количество нейронов уменьшается в каждом последующем слое. Его задачей является не распознавание объекта, а наоборот отбрасывание всех данных, не являющихся ключевыми для него. Другими словами он удаляет шум, сначала кодируя объект с помощью аппроксимации, а потом декодируя его обратно. Нейросеть для RTX Voice обучалась на сэмплах человеческого голоса, тренируясь отсекать все посторонние звуки, которые не относятся к речи. Если ее запустить наоборот, то она отфильтрует из звуковой дорожки все посторонние шумы, оставив только чистый голос. (Что-то похожее делали ретушеры при обработке фотографий, до того, как для этого были выпущены многочисленные плагины-шумодавы. Фотография уменьшалась в полтора-два раза, а потом увеличивалась обратно, в результате этой операции качество страдало не очень сильно, но зато заметно сглаживались шумы характерные для джпеговской компрессии ли скана с журнального оттиска.)

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

Нейросетка в действии: как сработало на обычном стриме и ASMR


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


Полное видео, как Аня настраивает VPN можно посмотреть на Vimeo. Версия для смелых без цензуры на onlyfans.

Видео записывалось смартфоном iPhone 11. На этом ролике последовательно демонстрируется запись звука с разных источников

  • встроенные микрофоны iPhone, все шумодавы выключены;
  • встроенные микрофоны iPhone, включен шумодав Nvidia RTX Voice;
  • микрофоны гарнитуры из ценового диапазона $100-$120, шумодавы выключены;
  • микрофоны гарнитуры из ценового диапазона $100-$120, включен шумодав Nvidia RTX Voice;
  • USB-микрофон, все шумодавы выключены;
  • USB-микрофон, включен шумодав Nvidia RTX Voice;

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

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

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

При царапании открытки, алгоритм распознает издаваемые звуки как речь и пытается ее восстановить. Получается забавно и немного пугающий эффект когда вместо скрипа мы слышим рваную и нечленораздельную речь (0:24 сек). Приходят мысли о бунте машин и зарождающемся нечеловеческом разуме.


Спектограммы из видео-ролика с ASMR


На спектрограммах наглядно видна работа TRX Voice звуковая дорожка из ролика, где девушка шептала и шуршала в микрофон.


Исходный звук, без вмешательство искусственного интеллекта


Звук, обработанный нейросетью

А всплеск на пустом участке справа это та самая говорящая открытка!

Для мурашек нейросети еще придется постараться


Nvidia RTX voice отлично справляется с обработкой шумов, если бюджет ограничен, а видео нужно спасать. Но для записи и обработки ASMR он не подходит.



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Как я монетизировал гнездо аистов

26.05.2021 10:15:20 | Автор: admin

Как возникла идея проекта

Аисты жили в нашей деревне давно. Пока работал, было не до них. Просто любовался красивыми птицами и все.

Фото 2012 из семейного архива ДмитрийФото 2012 из семейного архива Дмитрий

Как инженеру-экспериментатору всегда хотелось заглянуть в гнездо и увидеть подробности. Сколько там яиц, как появляется аистенок, чем их кормят. Логично, что надо установить видеонаблюдение. Времени для этого не было, знаний тоже и это откладывалось из года в год.

И только с выходом на пенсию появилось время для реализации этой идеи.

Было решено закрепить камеру наблюдения над гнездом и вывести сигнал на видеорегистратор.

Был сварен Z-образный кронштейн и аналоговая камера в апреле месяце 2020 стала фиксировать все, что происходит на высоте 9 метров в гнезде.

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

Тем временем прилетели аисты и начали откладывать яйца.

Я несмотря на 64 года вполне комфортно чувствую себя в соцсетях поэтому для продвижения проекта завел инстаграм @storks40, куда и стал выкладывать фото и видео из гнезда.

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

К моему удивлению вскоре на карточку стали поступать деньги. Денег хватило для оплаты мобильного интернета и даже для возмещения расходов на оборудование. А в конце сезона поступил разовый большой взнос на новую камеру к следующему сезону. И я уже решил в новом сезоне обязательно сделать прямые трансляции на YouTube.

Технические моменты.

Я не системный администратор и моя работа не связана с компьютерными сетями. В молодости программировал на Clipper summer87, Access 2.0. Это немного помогло и практически всю нужную информацию нашел в сети.

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

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

К новому сезону 2021года к дому в деревне был подключен проводной интернет и над гнездом вознеслась новая поворотная IP камера Hikvision с 4-х кратным зумом.

К дому был проведен проводной интернет 80 Mbps получен статический IP адрес. Это обязательное условие для удаленного доступа к камере без танцев с бубнами. Достаточно пробросить порт на роутере.

Камера передает rtsp поток, с которым теперь можно делать потоковое вещание на различные площадки. Иногда вел вещание на Youtube, FB,VK и Twith одновременно.

Для вещания в YouTube и другие сервисы нужен rtmp поток. Можно использовать программу по типу OBS, но мне это не подошло. Я решил работать с утилитой ffmpeg. Это бесплатная программа с широчайшим спектром функций по работе с видео. Не требует установки, запускается в командной строке, работает на всех трех крупных операционных системах.

В сети много ссылок с форматом ключей для обработки rtsp потока с видеокамер. Я использовал такой.

ffmpeg -re -rtsp_transport tcp -i "****rtsp ссылка****" -c:a libmp3lame -ab 128k -ar 44100 -c:v copy -threads 2 -bufsize 4000k -f flv -crf 0 -minrate 4000k -maxrate 5000k -tune zerolatency "rtmp://a.rtmp.youtube.com/live2/*ключ потока**"

Где брать rtsp ссылку? Ее надо искать на сайте производителя камеры. В нее вписывают логин и пароль камеры, номер канала.

Ссылка для Hikvision "rtsp://логин:пароь@IP_адрес_камеры/ISAPI/Streaming/Channels/101"

Прелесть в том, что для вещания в разные сервисы в этой команде нужно только менять в конце rtmp ссылку и ключ потока. Работать с ffmpeg очень удобно, хотя с начала это напрягало. Отвык от окна терминала. Пришлось вспомнить утраченные навыки работы с командной строкой и bat-файлами.

Какой компьютер нужен для круглосуточной трансляции?

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

Это тупиковый путь. Лучше сразу брать в аренду виртуальный сервер, тем более что есть варианты бесплатного доступа на 90 дней от google, amazon и microsoft.

Я подключил виртуальную машину от гугла https://cloud.google.com/ . Никогда раньше не имел дела с VPS, но легко разобрался за полчаса с настройкой по этому видео https://youtu.be/duKOkL1Sjww

Установил сервер c Microsoft, Windows Server, 2012 R2, 4 Гб память,SCSI 50Гб.

Для быстрого запуска ffmpeg cделал bat файл. Потом заметил, что иногда ffmpeg вылетает и трансляция виснет. Тогда в bat файле сделал цикл от 1 до 1000 и проблема исчезла.

При минимальной конфигурации VPS загружен только на 5%.

При регистрации аккаунта в cloud.google надо указывать данные реальной банковской карты. Для этого я завел виртуальную карту в тинькофф банке. Главное не забыть отключить услугу до окончания 90 дневного срока.

Запуск прямой трансляции на ютубе описан во многих видео, поэтому здесь об этом не буду.

Сезон 2021 года. Прямая круглосуточная трансляция на YouTube.

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

К прилету первого аиста его уже ждали в прямом эфире несколько десятков подписчиков канала на YouTube. Их было не больше 40 человек. Я сделал посты в соцсетях и новость о интересной трансляции очень быстро разошлась по местным СМИ. Газеты сделали хорошую рекламу на своих интернет ресурсах , а телеканалы помогли еще больше. (Это для меня было открытием. Люди сидели перед экраном где было только гнездо и просто общались в чате. А просмотры шли и подписчики прибавлялись.)

Трансляции уже велись круглосуточно. При этом я даже не задействовал свой компьютер. Был арендован бесплатный (на 90 дней) виртуальный сервер google, который прекрасно справляется с этой задачей.

Во время трансляции заметил в чате активного пользователя и сделал ее модератором чата. Это было неожиданно для Марины, но она согласилась и стала мне активно помогать в эфире.

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

Все это помогло меньше чем за месяц собрать 1000 подписчиков и 4000 минут просмотров. YouTube - дал добро на монетизацию.

Неочевидные плюсы прямых трансляций из гнезда аистов.

  • Не надо постоянно монтировать новые видео. Видео делает сама жизнь.

  • Круглосуточные трансляции дают много просмотров. Доход от рекламы растет.

  • Прямо во время трансляции можно делать подборку интересных кадров и тут же публиковать на канале. Это функция ютуба явно мало кому известна. Я так сделал очень много интересных видео.

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

  • Это очень познавательно. Хорошая камера дает возможность рассмотреть все подробности жизни. Увидеть как вылупляется птенец, чем их кормят. Разрушаются многие мифы о аистах.

  • Все это помогает развитию канала и росту просмотров.

Заставка к очередной трансляции Так Заставка к очередной трансляции Так

Через полтора месяца с начала трансляций уже каждый день приносил по 140 -210 рублей в день за счет рекламы. И эта цифра продолжает расти. А донаты полностью покрыли расходы и осталось на развитие проекта.

статистика на 26 мая 2021статистика на 26 мая 2021

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

Есть куда развиваться. Канал постоянно набирает просмотры и подписчиков. На 25 мая 2061подписчиков. А еще недавно в соседней речке завелись бобры и я уже строю планы по видеотрансляции из хатки бобров)

Ссылка на канал https://www.youtube.com/channel/UCwgh6pJpiVlVObWaXKLjGqw

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

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

Подробнее..

Обзор роликов об играх с Game Trailer Challenge и их разбор с Alconost

27.05.2021 20:23:24 | Автор: admin

Этой весной мы вошли в жюри Game Trailer Challenge, в рамках которого гейм-девелоперы создавали ролики о своих играх. Мы посмотрели трейлеры и тизеры, сделанные разработчиками, внимательно оценили каждое видео и хотим показать вам 10 наиболее впечатливших нас роликов. Расскажем, что именно, на наш взгляд, сделало их классными, и что могло бы помочь им стать ещё лучше.

Идейный вдохновитель и спонсор челленджа польская компания Games Operators, издатель игр. А проводили челлендж основатели Game Industry Conference мероприятия для разработчиков игр, которое проводится в польской Познани с 2014 года. В этом году конференция должна состояться 21-24 октября.

Почти в топе

4 ролика, которым не хватило совсем чуть-чуть

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

Подчеркнём: оценки здесь и далее баллы от Alconost. В жюри челленджа, помимо нас, было ещё 11 экспертов, и их оценки не обязательно совпадали с нашими.

Трейлер игры Carebotz: 7 баллов

Релиз этого космического шутера, разработанного Glasscannon Studio из Венгрии, состоялся в Steam 7 мая.

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

Только вот ключевой объект, за которым нужно следить в геймплее космический корабль игрока, очень маленький, он не бросается в глаза. Его нужно буквально искать в кадре взглядом. Это особенно трудно делать в сценах, где есть другие объекты похожие и такие же мелкие: визуальное различие не считывается. Чтобы зрителю было проще сфокусировать внимание на нашем корабле, его можно было бы выделить: например, увеличить или подсветить.

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

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

Тизер об игре Pale Night: 7 баллов

Этот минималистичный 2D-платформер от парижской инди-студии Pretty French Games, основанной бывшим инженером машинного обучения из Nvidia Симоном Андерсеном, должен выйти в Steam в сентябре 2021-го.

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

Но главная фишка игры раскрывается лишь на 17-й секунде видео. Все ли досмотрят до этого момента?

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

Даже несмотря на немного однотипные фрагменты геймплея, мы бы назвали тизер Pale Night довольно удачным примером ролика об игре. В нём отлично продуман сюжет, чувствуется логичная структура. Кажется, будто смотришь мультфильм с завязкой, кульминацией и развязкой.

Трейлер Covert: 8 баллов

Это видео от ребят из польской Baseball Team произвело на нас впечатление. Хотя сама игра находится на ранней стадии разработки, видно, что ролик делали с душой и уделили его созданию немало времени.

Первые 20 секунд трейлера смотрятся на одном дыхании. Движения камеры, отбор геймплейных моментов, надписи в кадре, музыка почти всё просто огонь! Весь видеоряд чётко синхронизирован с битом фоновой музыки: это отличный приём, чтобы погрузить в атмосферу и зацепить зрителя. Ролик выглядит многообещающим и задаёт высокую планку в плане ожиданий от самой игры.

Недостатки? К сожалению, они есть, и столкновение с ними превращает просмотр в американские горки. Практически безупречная анимация внезапно перебивается то фрагментом с сильной пикселизацией, то записью геймплея с неаккуратным UI, то сценой с недоработанной персонажной анимацией. На фоне блестяще проработанных сцен такие моменты особенно бросаются в глаза.

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

Тем не менее, под натиском грозных марширующих хомяков, мы оценили этот трейлер на 8.

Трейлер для игры Wayfarers: Call of Osiris: 8 баллов

Студия ActaLogic из Любляны планирует выпустить этот археологический экшен для PlayStation 4, Xbox One и ПК (Steam) в 3-4 квартале 2021-го.

Сценарий вот чем особенно выделяется этот ролик. Будто бы игра была использована как отправная точка для создания короткометражного анимационного фильма. Качество картинки отметим отдельно. Заметно, что разработчики тщательно настроили локации и продумали траекторию движений камеры, прежде чем приступить к съёмке.

И обратите внимание, как здорово в плане драматизма работает пауза на 0:14: она погружает в сюжет и заставляет смотреть дальше. К слову, первые 15 секунд видео вполне можно использовать и как отдельный ролик-тизер.

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

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

Топ-5 игровых роликов по версии Alconost

Эти видео мы оценили на 9 и 10.

Трейлер игры words: 9 баллов

Смотрите этот ролик со звуком: музыка в нём очень цепляет. Анимация тоже на уровне: продуманные планы, просчитанные движения камеры. Комбинация быстрых движений и почти статичных кадров, как на 20-23-й секундах, подчёркивает ритм анимированных событий. При этом, в ролике нет хаотичного действия: весь моушен очень хорошо сбалансирован.

А ещё в ролике есть ярко выраженная кульминация. Это на 32-35-й секундах, когда предмет из окружения вдруг становится интерактивным, а напряжённый и интенсивный фрагмент саундтрека сменяется плавным перебором струн. Как будто мы полминуты бежали по тёмным комнатам вместе с невидимым героем ролика и наконец добрались до безопасного места, где можно выдохнуть и забыть о тревогах. Эта метаморфоза подчёркивается всеми возможными выразительными средствами, вплоть до смены характер освещения в сцене.

Что можно улучшить? На наш взгляд, 10-секундная экспозиция названия игры это перебор. Особенно с учётом того, что после демонстрации названия в ролике есть небольшой тизерный приём. Зрители могут просто не досмотреть до него, приняв долго висящее на экране название за закрывающую заставку. Время показа можно сократить, а можно занять другими слоганами с игрой слов, ведь это подходит игре по духу. А логотипы из конца открывающей сцены мы бы перенесли в конец закрывающей.

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

Трейлер игры EXISTENTIAL: 9 баллов

Игра, которой посвящён трейлер, создана инди-разработчиком из Турции в рамках 72-часового геймдев-челленджа Atom GameJam. Кстати, words (предыдущий пункт нашего рейтинга) тоже его работа, и в роликах об обеих играх можно заметить кое-что общее: гармоничное сочетание аудио- и видеоряда, синхронизация анимации с ритмом музыки, а главное только значимые фрагменты геймплея, которые действительно помогают понять контекст игровой ситуации. Этот роликдоказывает: ввести потенциального пользователя в курс дела, передать атмосферу игры и намекнуть, что именно нужно будет делать в ходе прохождения, можно быстрее, чем за 15 секунд.

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

Тизер для игры Sunfall: Children of Adiona: 9 баллов

Первые 30 секунд ролика в техническом плане близки к совершенству. Качество картинки, скорость и траектория камеры, постобработка роботизированного голоса, реплики персонажей и звуковые эффекты всё прекрасно. Разве что 5-секундную белую засветку в самом начале мы бы чуть подсократили.

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

О самой игре, которую разрабатывает Tractor Set GO! геймдев-студия из Румынии, пока известно не очень много. Если предположить, что в игре будет больше контента и, когда игра будет ближе к релизу, ролик решат немного дополнить думаем, он отлично справится с задачей мотивировать потенциальных пользователей установить игру.

Трейлер игры Under the Counter: 9 баллов

Если бы в рамках челленджа была номинация Приз симпатий жюри, наши сердечки улетели бы к этому видео. Поздравляем варшавскую студию Korba Games с отличной работой! Релиз их визуальной новеллы Under the Counter в Steam запланирован на 30 июня 2021.

Вот что нам особенно понравилось в этом видео:

  1. Голосовая озвучка. Это не просто начитка текста, это актёрская игра как раз то, что нужно трейлеру-истории. Манера чтения, логические паузы, смысловые акценты всё на месте, и всё дозировано: никакого переигрывания.

  2. Визуальный стиль. Арты разнообразные, но всё в едином стиле. Пунцовые тона в некоторых сценах и неоновая вывеска в логотипе аккуратно и уместно разбавляют цветовую палитру ролика, в целом довольно спокойную.

  3. Приём с камерой, проезжающей вниз, за пределы ранее видимого в кадре. Он отлично перекликается с названием игры (буквально, Под барной стойкой) и создаёт атмосферу тайны, которая вот-вот приоткроется. Отличный пример осмысленного моушен-дизайна.

  4. Общая плавность анимации, её размеренность. Темп видеоряда соответствует темпу аудиоряда. События в кадре не мешают следить за сюжетом, не отвлекают от сути.

  5. Музыка и звуковые эффекты: всё уместно, уравновешенно и отлично сведено.

Из моментов, которые мы бы улучшили, отметим сцену на 0:33-0:47: анимация рук главного героя всё же могла бы быть немного живее. Но главное как нам не хватило акцента на 1:05, когда камера фирменным движением плывёт вниз и оказывается, что красотка за барной стойкой вовсе не та, за кого себя выдаёт! Мы почти почувствовали мурашки по коже в этот момент, и как уместно было бы небольшое зависание камеры на этом моменте, лаконичный анимированный эффект или особый звук! Не то чтобы это было обязательным: ролик и без того выглядит абсолютно целостным и захватывающим. Но такой приём мог бы стать той самой вишенкой на торте, которая дала бы зрителю ещё больше эмоций.

Трейлер для игры #DRIVE: 10 баллов

#DRIVE сингл-плеерная гонка от польской геймдев-студии Pixel Perfect Dude. Игра доступна не только в привычных Google Play и App Store, но ещё и в Nintendo eShop.

Мы поставили этому видео наивысший балл. Проработка сценария и визуального ряда, дикторская озвучка, музыка и звуковые эффекты всё отлично. Если вы планируете сделать ролик о своей игре самостоятельно возьмите это видео на заметку.

Бонус: отличный тизер к несуществующей игре

Из 20 роликов, которые мы оценивали в рамках челленджа, высшую оценку от нас получили два видео. О первом мы рассказали выше, а вот второе. Мы вынесли его в отдельную категорию, потому что это видео единственное в нашей подборке, которое показывает несуществующую игру. (Условия челленджа позволяли такое.)

Тизер для игры Fast Like Hell: 10 баллов

Этот тизер выглядит так, будто он был сделан на одном дыхании. Оцените динамику действия, проработанность анимации, музыку, юмор всё сочетается отлично! Хотя мы бы немного больше внимания уделили дизайну слоганов (серые плашки и чёрный шрифт без изысков выглядят простовато, особенно в контексте сочного, глянцевого арта) и добавили бы звуковые эффекты (они просто просятся в ролик, особенно на 0:19-0:23), даже без этого тизер выглядит лёгким для восприятия, живым и очень естественным.

Кто выиграл челлендж

Помимо нас, в жюри было ещё 11 экспертов профессионалов в геймдеве. По общему мнению жюри, челлендж выиграли два видео: трейлер Under the Counter и тизер Fast Like Hell. Желаем ребятам из Korba Games успехов в развитии игры Under the Counter и надеемся, что положительный отклик жюри вдохновит студию Berdo Games на разработку Fast Like Hell!

Топ роликов по общему мнению жюри челленджа

А вот и рейтинг роликов согласно оценкам всех членов жюри.

Место

Игра

Ролик

1

Under the Counter

https://youtu.be/sT9cP0sbqeo

1

Fast Like Hell

https://youtu.be/hAG4CziFBzA

2

#DRIVE

https://youtu.be/yi4gOixZpck

3

Pale Night

https://youtu.be/wwbizUvY-5s

4

Wayfarers: Call of Osiris

https://youtu.be/IESRqhqGmPM

5

Sunfall: Children of Adiona

https://youtu.be/ExkVPoXxZRE

6

Carebotz

https://youtu.be/8jJNGujedXo

7

words

https://youtu.be/ACFS1eMockI

8

Existential

https://youtu.be/4xqYQKyMUwQ

Хотя положение роликов в общем рейтинге отличается от их позиций в нашем личном топе, в 9 случаях из 10 членам жюри понравились те же ролики, что и нам.

Хотите сделать трейлер или тизер о своей игре?

Создание роликов для игр наша работа. Будем рады сделать эффектное видео о вашем проекте! А если вы хотите попробовать сделать ролик самостоятельно надеемся, вам помогут наши материалы:

Об авторе

Статья написана вAlconost. Мы уже 8 летсоздаём видеоролики: рекламные и обучающие, для игр и приложений, продуктов и компаний. Ещё мы занимаемсялокализацией игр,приложений и сервисовна 80+ языков.

Подробнее..

Перевод Каждый браузер видит цвета видео по-разному

01.06.2021 10:11:39 | Автор: admin

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

В преобразовании значения RGB-триплета в конкретную длину волны света задействовано множество систем. Это преобразование должно быть стандартизовано, чтобы всё ПО, все декодеры видео, видеокарты и мониторы (даже изготовленные разными производителями в разные десятилетия) могли создавать одинаковые результаты по одинаковым входным данным. Для решения этой задачи были разработаны цветовые стандарты. Однако со временем дисплеи и другие технологии развивались. Телевидение стало цифровым, начали применять сжатие, а мы отказались от ЭЛТ в пользу LCD и OLED. Новое оборудование было способно отображать больше цветов при большей яркости, но получаемые им сигналы по-прежнему были адаптированы под возможности старых дисплеев.

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

Это работающее решение, но оно усложняет процесс создания видео. На каждом этапе процесса захвата, записи, редактирования, кодирования, декодирования, композитинга и отображения необходимо учитывать используемое цветовое пространство. И если хотя бы на одном этапе процесса используется старое оборудование, или есть баг ПО, из-за которого не учитывается цветовое пространство, то в результате может получиться некорректное изображение. Видео всё равно можно будет смотреть, однако цвета могут оказаться слишком тёмными или бледными.

Сегодня двумя самыми популярными цветовыми пространствами являются BT.601 (также называемое smpte170m; в статье я буду использовать оба этих названия), которое стало стандартом для SD-контента, и BT.709, которое стало стандартом HD-контента. Существует также BT.2020, которое становится популярнее благодаря HDR- и UHD-контенту. Стоит заметить, что разделение на HD/SD здесь немного ошибочно. Технические ограничения отсутствуют, это просто традиционный подход. HD-контент можно кодировать в BT.601, а SD-контент в BT.709. Если взять видеофайл с разрешением 1080p и уменьшить его до 480p, то цветовое пространство не изменится автоматически. Смена цветового пространства это дополнительный этап, который выполняется как часть процесса.

Что же происходит, если процесс выполняется неправильно? Давайте проведём эксперимент.

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

Для начала я создам с помощью ffmpeg простой тестовый файл:

ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -color_range pc -y 601.mp4

Краткое объяснение этой команды:

ffmpeg исполняемый файл

-f rawvideo сообщает программе ffmpeg, что я передаю ей сырые пиксельные данные, а не видеофайл.

-s 320x240 разрешение выходных данных. Можно использовать любое значение, потому что все входящие значения будут одинаковыми. Но благодаря маленькому размеру мы ускоряем процесс.

-pix_fmt yuv420p указываем формат пикселей входящих данных. Yuv420p это способ описания пикселей. Здесь важно отметить, что значение yuv(0,0,0) представляет собой оттенок зелёного. Я использую этот формат в противовес RGB, поскольку он является самым популярным форматом, используемым в цифровом видео.

-t 1 ограничиваем входящие данные 1 секундой

-i /dev/zero это файл входящих данных. /dev/zero это виртуальный файл, существующий на всех компьютерах mac. Это бесконечно длинный файл, состоящий из одних нулей.

-an обозначает, что выходные данные не должны содержать звука.

-vcodec libx264 используем для сжатия видео потрясающую библиотеку libx264.

-profile:v baseline используем базовый профиль h.264. Он отключает некоторые расширенные возможности h.264, но в этом тесте они нам не понадобятся.

-preset:v placebo сообщаем библиотеке libx264, что она может потратить дополнительные ресурсы процессора на кодирование видео с повышенным качеством. В реальной ситуации эту опцию выбирать не стоит, потому что кодирование занимает КУЧУ времени и обеспечивает минимальное улучшение качества. Нам она подходит, потому что у меня мало входящих данных.

-color_range pc один компьютерный байт может иметь значения от 0 до 255. При оцифровке аналогового видео используется интервал 16-235. Он был выбран из-за того, как телевизор интерпретирует очень тёмные и очень яркие сигналы. Так как мы используем цифровой источник, я выбрал значение pc, а не tv.

-crf 18 опция постоянного коэффициента потока (constant rate factor) сообщает libx264, что нужно создать высококачественный видеофайл и использовать любое количество бит, необходимое для обеспечения качества 18. Чем меньше число, тем выше качество. 18 это очень высокое качество.

-y даёт ffmpeg разрешение на перезапись файла, если он существует.

601.mp4 имя получившегося файла.

Эта команда создаёт файл 601.mp4 длительностью 1 секунду, который можно открывать и воспроизводить. После выполнения этой команды мы можем проверить, что ffmpeg не исказил значения пикселей, выполнив следующую команду и изучив выходные данные:

ffmpeg -i 601.mp4 -f rawvideo - | xxd

00000000: 0000 0000 0000 0000 0000 0000 0000 0000
00000010: 0000 0000 0000 0000 0000 0000 0000 0000
00000020: 0000 0000 0000 0000 0000 0000 0000 0000
00000030: 0000 0000 0000 0000 0000 0000 0000 0000
00000040: 0000 0000 0000 0000 0000 0000 0000 0000
00000050: 0000 0000 0000 0000 0000 0000 0000 0000
...
...

Эти данные в шестнадцатеричном виде показывают, что все значения пикселей после декодирования равны нулю.

При рендеринге видео в Safari мы получаем такой скриншот:


Возникает вопрос: что это за цветовое пространство? Я назвал файл 601.mp4, но нигде в команде я не указывал цветового пространства, так как же Safari узнал, какой оттенок зелёного нужно рендерить? Откуда браузер знает, что yuv(0,0,0) должно быть равно rgb(0,135,0)? Очевидно, что существует алгоритм для вычисления этих значений. На самом деле, это простое матричное умножение. (Примечание: в некоторых форматах пикселей, в том числе и в yuv420p, для преобразования требуется этап пре- и постпроцессинга, но в этой демонстрации мы опустим такие тонкости). Для каждого цветового пространства имеется собственная матрица. Так как мы не задавали матрицу цветового пространства при кодировании видео, Safari просто делает предположение. Мы можем перебрать все матрицы, умножить все значения RGB на обратные матрицы и посмотреть, чему же они соответствуют, но давайте попробуем использовать более визуальный подход и посмотреть, удастся ли нам разобраться, что делает Safari.

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

ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -colorspace smpte170m -color_primaries smpte170m -color_trc smpte170m -color_range pc -y 601vui.mp4

Команда ffmpeg осталась практически такой же, но я добавил следующее:

-color_trc smpte170m

-colorspace smpte170m

-color_primaries smpte170m


Это метаданные цветового пространства, в который будет кодироваться файл. Я не буду объяснять различия между этими опциями, потому что для этого понадобится ещё одна статья. Пока мы просто задаём всем им нужное нам цветовое пространство. smpte170m это то же самое, что и BT.601.

Указание цветового пространства не влияет на способ кодирования файла, значения пикселей по-прежнему кодируются как yuv(0,0,0). Чтобы убедиться в этом, мы можем выполнить для нового файла команду ffmpeg -i zero.mp4 -f rawvideo - | xxd. Флаги цветового пространства не игнорируются, однако просто записываются в несколько битов внутри раздела video usability information (VUI) в заголовке видеопотока. Теперь декодер будет искать VUI и использовать его для загрузки нужной матрицы.

А вот результат:


И с VUI, и без него видео рендерятся с одинаковым цветом. Давайте попробуем файл BT.709:

ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -y 709.mp4

Новые опции:

-i 601vui.mp4 используем в качестве источника видео прежний файл 601vui.mp4

-vf "colorspace=all=BT.709" сообщаем ffmpeg, что нужно использовать видеофильтр цветового пространства для изменения значений пикселей. Это похоже на умножение матрицы для преобразования из yuv в rgb, но матрица имеет другие коэффициенты. all это сокращение для одновременного задания color_primaries, colorspace и color_trc.

Здесь мы берём видео 601vui.mp4 и используем фильтр цветового пространства для преобразования в BT.709. Фильтр цветового пространства может считать из vui файла 601vui.mp4 цветовое пространство входящих данных, поэтому нам достаточно только указать цветовое пространство, которое мы хотим получить на выходе.

Выполнив для этого файла команду ffmpeg -i 709.mp4 -f rawvideo - | xxd, мы получаем после преобразования цветового пространства значения пикселей yuv(93,67,68). Однако при рендеринге файла он должен выглядеть так же. Стоит заметить, что окончательные результаты могут и не быть идентичными, потому что мы продолжаем использовать 24 бита для кодирования каждого пикселя, а BT.709 имеет чуть больший диапазон цветов. Следовательно, некоторые цвета в BT.709 не сопоставляются точно с BT.601, и наоборот.

Посмотрев на результат, можно чётко заметить, что что-то не так. Новый файл рендерится со значениями rgb, равными 0,157,0 гораздо ярче, чем входящий файл.

image

Давайте внимательно изучим свойства файла при помощи приложения ffprobe:

ffprobe 601vui.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc, smpte170m), 320x240, 9 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)

И

ffprobe 709.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc), 320x240, 5 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)

Основная часть информации здесь для нас неважна, но мы заметим, что 601vui.mp4 имеет формат пикселей yuvj420p(pc, smpte170m). Так мы понимаем, что файл имеет правильный VUI. Но 709.mp4 содержит только yuvj420p(pc). Похоже, метаданные цветового пространства не были включены в выходной файл. Даже несмотря на то, что фильтр цветового пространства смог прочитать исходное цветовое пространство, и мы явным образом указали новое пространство, программа ffmpeg не записала правильный vui в конечный файл.

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

Обойти её можно, добавив метаданные цветового пространства вручную:

ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -colorspace bt709 -color_primaries bt709 -color_trc bt709 -color_range pc -y 709vui.mp4

В результате значение цвета в 709vui.mp4 будет равно rgb(0,132,0). Яркость зелёного канала чуть меньше, чем в 601vui.mp4, но поскольку преобразование цветового пространства происходит с потерями, а результат меня устраивает, то назовём это успехом.

Из этого мы можем прийти к заключению, что когда в файле не указано цветовое пространство, Safari считает, что это BT.601. И со стороны Safari это очень хорошее допущение. Но как сказано выше, BT.601 это стандарт SD-видео, а BT.709 стандарт для HD. Давайте проверим HD-видео с VUI и без него, и посмотрим, как их рендерит Safari. Я использовал те же команды ffmpeg, только изменил разрешение на 1920x1080.

image

И в SD, и в HD цвет рендерится одинаково. Делая предположение о цветовом пространстве, Safari не учитывает разрешение. Apple уже давно работает в пространстве медиа и издательского дела, поэтому я ожидал, что продукт этой компании обеспечит достойные результаты. Но если даже в Safari всё устроено настолько хитро, то интересно, как обстоит ситуация в других браузерах.

Chrome:


Chrome рендерит видео 601 чуть более тёмным, чем Safari, но 709 выглядит так же. Подозреваю, что разница вызвана оптимизацией скорости в вычислениях с плавающей запятой, но для нашего теста это несущественно.

По опыту я знаю, что когда в настройках отключено аппаратное ускорение, Chrome выполняет рендеринг иначе:


Изучив этот результат, мы увидим, что значения в 601 рендерятся очень похожими на предыдущие, но файлы 709 рендерятся так, как будто в них нет VUI. Из этого мы можем заключить, что Chrome при отключенном аппаратном ускорении просто игнорирует VUI и рендерит все файлы, как будто они имеют формат 601. Это означает, что все файлы 709 будут воспроизводиться некорректно.

И наконец, давайте изучим Firefox:


Здесь нужно разобрать многое. Так как 709.mp4 и 709vui.mp4 выглядят одинаково, можно прийти к заключению, что при отсутствии VUI браузер Firefox предполагает формат BT.709. Правильный рендеринг 601vui.mp4 означает, что для контента BT.601 раздел VUI учитывается. Однако когда файл BT.601 без VUI рендерится как 709, то становится очень тёмным. Очевидно, что невозможно отрендерить картинку правильно без всей необходимой информации, однако выбранный Firefox способ искажает цвет сильнее, чем выбранные браузерами Safari и Chrome.

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

Microsoft Edge:


Похоже, что Edge (по крайней мере, на моём компьютере) просто игнорирует VUI и рендерит всё как 601.

Chrome (со включенным аппаратным ускорением):


Ситуация не очень отличается от Mac. При наличии VUI он обрабатывается правильно, но если его нет, для SD-контента предполагается формат BT.601, а для HD-контента BT.709. Это единственный браузер, в котором я такое видел, но в этом есть определённая логика. Так как рендеринг выполняется иначе, чем на Mac, то подозреваю, что дело в ОС или, что более вероятно, в чём-то на уровне драйверов видеокарты, и этот выбор сделан не командой разработчиков Chrome.

Firefox ведёт себя так же, как и на Mac.


Что касается Linux, iOS, Android, Roku, Fire TV, смарт-телевизоров, игровых консолей и т.д., то я оставлю это в качестве упражнения для читателя.

Чему же мы научились? Самое главное: всегда указывайте в своих видео метаданные цветового пространства. Если вы пользуетесь ffmpeg и не задаёте флаги цвета, то вы работаете неправильно. Во-вторых, хотя ffmpeg и является потрясающей программой, её популярность, простота использования и неудачно выбранные стандартные параметры сослужили плохую службу. Никогда не стоит допускать, что ПО достаточно умно, чтобы разобраться в этом самостоятельно. Руководителям проектов Ffmpeg, Google, Mozilla, Microsoft (и, вероятно, Nvidia и AMD) нужно собраться и вместе выбрать единый способ. Я понимаю, что здесь нет хорошего решения, но плохое и предсказуемое лучше, чем плохое и случайное. Лично я рекомендую всегда предполагать формат BT.601, если раздел VUI отсутствует. Это создаёт наименьшую степень искажений. Можно выбрать для согласования этого стандарта FOMS, или даже AOM, поскольку эти организации имеют довольно неплохое представительство.

И напоследок: если у вас есть видео без информации о цвете и вам нужно его преобразовать или отрендерить, то удачи!



На правах рекламы


VDSina предлагает недорогие серверы с посуточной оплатой. Интернет-канал для каждого сервера 500 Мегабит, защита от DDoS-атак включена в тариф, возможность установить Windows, Linux или вообще ОС со своего образа, а ещё очень удобная панель управления серверами собственной разработки. Давно пора попробовать ;)

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Автоматизируй это, или Контейнерные перевозки Docker для WebRTC

11.06.2021 08:06:47 | Автор: admin

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

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

Путей решения этой задачи может быть несколько:

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

  2. Использовать скрипты. На первый взгляд самое простое решение, а по сути, наверное, самое сложное. В этом случае, мы не тащим с собой операционку и дополнительное ПО. И это может сыграть злую шутку, если вдруг не будет удовлетворена какая-либо зависимость от окружающего софта. Попадется случайно в репозитории условный Python не той версии и все!

  3. Запускать основной продукт в контейнерах. Это самое современное на сегодняшний день решение. Контейнер - это некая изолированная от внешних факторов среда. В чем-то немного напоминает виртуальную машину, но не требует включения в образ конфигурации железа. В работе так же, как и виртуальная машина, использует ресурсы хоста. Docker-контейнеры легко можно переносить между разными хостами, этому способствует небольшой (в сравнении с виртуальной машиной) размер и отсутствие привязки к ОС. Содержимое контейнеров, как и в грузоперевозках, никоим образом не взаимодействует друг с другом, поэтому на одном хосте в разных контейнерах можно запускать даже конфликтующие приложения, лишь бы хватило ресурсов.

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

Стриминг без использования контейнеров:

Стриминг с использованием контейнеров:

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

  • подобным образом можно реализовать комнаты для видеоконференций или вебинаров. Одна комната - один контейнер. ;

  • организовать систему видеонаблюдения за домами. Один дом - один контейнер;

  • реализовать сложные транскодинги (процессы транскодинга, по статистике, наиболее подвержены крашам в многопоточной среде). Один транскодер - один контейнер.

    и т.п.

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

Почему все таки контейнеры, а не виртуалки?

Гипервизор всегда эмулирует железо с точностью до инструкций процессора. Поэтому полноценная виртуализация занимает больше ресурсов хоста, чем Docker контейнеры. Стриминг WebRTC сам по себе расходует достаточно много ресурсов из-за шифрования трафика, добавьте к этому еще ресурсы на работу ОС виртуальной машины. Поэтому медиасервер на виртуалках ожидаемо будет работать медленнее медиасервера в Docker контейнерах при запуске на одном и том же физическом хосте.

Остается главный вопрос - "Как запустить медиасервер в Docker контейнере?

Разберем на примере Web Call Server.

Легче легкого!

В Docker Hub уже загружен образ Flashphoner Web Call Server 5.2.

Развертывание WCS сводится к двум командам:

  1. Загрузить актуальную сборку с Docker Hub

    docker pull flashponer/webcallserver
    
  2. Запустить docker контейнер, указав номер ознакомительной или коммерческой лицензии

    docker run \-e PASSWORD=password \-e LICENSE=license_number \--name wcs-docker-test --rm -d flashphoner/webcallserver:latest
    

    где:

    PASSWORD - пароль на доступ внутрь контейнера по SSH. Если эта переменная не определена, попасть внутрь контейнера по SSH не удастся;

    LICENSE - номер лицензии WCS. Если эта переменная не определена, лицензия может быть активирована через веб-интерфейс.

Но, если бы все было настолько просто не было бы этой статьи.

Первые сложности

На своей локальной машине с операционной системой Ubuntu Desktop 20.04 LTS я установил Docker:

sudo apt install docker.io

Создал новую внутреннюю сеть Docker с названием "testnet":

sudo docker network create \ --subnet 192.168.1.0/24 \ --gateway=192.168.1.1 \ --driver=bridge \ --opt com.docker.network.bridge.name=br-testnet testnet

Cкачал актуальную сборку WCS с Docker Hub

sudo docker pull flashphoner/webcallserver

Запустил контейнер WCS

sudo docker run \-e PASSWORD=password \-e LICENSE=license_number \-e LOCAL_IP=192.168.1.10 \--net testnet --ip 192.168.1.10 \--name wcs-docker-test --rm -d flashphoner/webcallserver:latest

Переменные здесь:

PASSWORD - пароль на доступ внутрь контейнера по SSH. Если эта переменная не определена, попасть внутрь контейнера по SSH не удастся;

LICENSE - номер лицензии WCS. Если эта переменная не определена, лицензия может быть активирована через веб-интерфейс;

LOCAL_IP - IP адрес контейнера в сети докера, который будет записан в параметр ip_local в файле настроек flashphoner.properties;

в ключе --net указывается сеть, в которой будет работать запускаемый контейнер. Запускаем контейнер в сети testnet.

Проверил доступность контейнера пингом:

ping 192.168.1.10

Открыл Web интерфейс WCS в локальном браузере по ссылке https://192.168.1.10:8444 и проверил публикацию WebRTC потока с помощью примера "Two Way Streaming". Все работает.

Локально, с моего компьютера на котором установлен Docker, доступ к WCS серверу у меня был. Теперь нужно было дать доступ коллегам.

Замкнутая сеть

Внутренняя сеть Docker является изолированной, т.е. из сети докера доступ "в мир" есть, а "из мира" сеть докера не доступна.

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

Конечно же нет. Путем курения мануалов был найден ответ. Нужно пробросить порты. Причем проброс портов нужен не на сетевом маршрутизаторе, а в самом Dockere.

Отлично! Список портов известен. Пробрасываем:

docker run \-e PASSWORD=password \-e LICENSE=license_number \-e LOCAL_IP=192.168.1.10 \-e EXTERNAL_IP=192.168.23.6 \-d -p8444:8444 -p8443:8443 -p1935:1935 -p30000-33000:30000-33000 \--net testnet --ip 192.168.1.10 \--name wcs-docker-test --rm flashphoner/webcallserver:latest

В этой команде используем следующие переменные:

PASSWORD, LICENSE и LOCAL_IP мы рассмотрели выше;

EXTERNAL_IP IP адрес внешнего сетевого интерфейса. Записывается в параметр ip в файле настроек flashphoner.properties;

Так же в команде появляются ключи -p это и есть проброс портов. В этой итерации используем ту же сеть "testnet", которую мы создали раньше.

В браузере на другом компьютере открываю https://192.168.23.6:8444 (IP адрес моей машины с Docker) и запускаю пример "Two Way Streaming"

Web интерфейс WCS работает и даже WebRTC трафик ходит.

И все было бы прекрасно, если бы не одно но!

Ну что ж так долго!

Контейнер с включенным пробросом портов запускался у меня около 10 минут. За это время я бы успел вручную поставить пару копий WCS. Такая задержка происходит из-за того, что Docker формирует привязку для каждого порта из диапазона.

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

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

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

Запускаем контейнер в сети хоста (на это указывает ключ --net host)

docker run \-e PASSWORD=password \-e LICENSE=license_number \-e LOCAL_IP=192.168.23.6 \-e EXTERNAL_IP=192.168.23.6 \--net host \--name wcs-docker-test --rm -d flashphoner/webcallserver:latest

Отлично! Контейнер запустился быстро. С внешней машины все работает - и web интерфейс и WebRTC трафик публикуется и воспроизводится.

Потом я запустил еще пару контейнеров. Благо на моем компьютере несколько сетевых карт.

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

Рабочий вариант

Начиная с версии 1.12 Docker предоставляет два сетевых драйвера: Macvlan и IPvlan. Они позволяют назначать статические IP из сети LAN.

  • Macvlan позволяет одному физическому сетевому интерфейсу (машине-хосту) иметь произвольное количество контейнеров, каждый из которых имеет свой собственный MAC-адрес.

    Требуется ядро Linux v3.93.19 или 4.0+.

  • IPvlan позволяет создать произвольное количество контейнеров для вашей хост машины, которые имеют один и тот же MAC-адрес.

    Требуется ядро Linux v4.2 + (поддержка более ранних ядер существует, но глючит).

Я использовал в своей инсталляции драйвер IPvlan. Отчасти, так сложилось исторически, отчасти у меня был расчет на перевод инфраструктуры на VMWare ESXi. Дело в том, что для VMWare ESXi доступно использование только одного MAC-адреса на порт, и в таком случае технология Macvlan не подходит.

Итак. У меня есть сетевой интерфейс enp0s3, который получает IP адрес от DHCP сервера.

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

Что бы этого избежать нужно зарезервировать часть диапазона подсети для использования Docker. Это решение состоит из двух частей:

  1. Нужно настроить службу DHCP в сети таким образом, чтобы она не назначала адреса в некотором определенном диапазоне.

  2. Нужно сообщить Docker об этом зарезервированном диапазоне адресов.

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

А вот как сообщить Docker, какой диапазон для него выделен, разберем подробно.

Я ограничил диапазон адресов DHCP сервера так, что он не выдает адреса выше 192.168.23. 99. Отдадим для Docker 32 адреса начиная с 192.168.23.100.

Создаем новую Docker сеть с названием "new-testnet":

docker network create -d ipvlan -o parent=enp0s3 \--subnet 192.168.23.0/24 \--gateway 192.168.23.1 \--ip-range 192.168.23.100/27 \new-testnet

где:

ipvlan тип сетевого драйвера;

parent=enp0s3 физический сетевой интерфейс (enp0s3), через который будет идти трафик контейнеров;

--subnet подсеть;

--gateway шлюз по умолчанию для подсети;

--ip-range диапазон адресов в подсети, которые Docker может присваивать контейнерам.

и запускаем в этой сети контейнер с WCS

docker run \-e PASSWORD=password \-e LICENSE=license_number \-e LOCAL_IP=192.168.23.101 \-e EXTERNAL_IP=192.168.23.101 \--net new-testnet --ip 192.168.23.101 \--name wcs-docker-test --rm -d flashphoner/webcallserver:latest

Проверяем работу web интерфейса и публикацию/воспроизведение WebRTC трафика с помощью примера "Two-way Streaming":

Есть один маленький минус такого подхода. При использовании технологий Ipvlan или Macvlan Docker изолирует контейнер от хоста. Если, например, попробовать пропинговать контейнер с хоста, то все пакеты будут потеряны.

Но для моей текущей задачи запуска WCS в контейнере это не критично. Всегда можно запустить пинг или подключиться по ssh с другой машины.

Используя технологию IPvlan на одном Docker хосте можно поднять необходимое количество контейнеров. Это количество ограничено только ресурсами хоста и, частично, сетевой адресацией конкретной сети.

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

Ссылки

WCS в Docker

Документация по развертыванию WCS в Docker

Образ WCS на DockerHub

Подробнее..

Как мы построили Computer Vision из подручных материалов, чтобы сделать гифки

17.06.2021 14:15:17 | Автор: admin

Меня зовут Денис Власов, я Data Scientist в Учи.ру. С помощью моделей машинного обучения из записей онлайн-уроков мы сделали гифки последовательность из нескольких кадров с наиболее яркими эмоциями учеников. Эти гифки получили их родители в e-mail-рассылке. Вместе с Data Scientist @DariaV Дашей Васюковой расскажем, как без экспертизы в Computer Vision, а только с помощью открытых библиотек и готовых моделей сделать MVP, в основе которого лежат low-res видео. В конце бонус виджет для быстрой разметки кадров.

Откуда у нас вообще возникла мысль распознавать эмоции? Дело в том, что мы в Учи.ру развиваем онлайн-школу Учи.Дома сервис персональных видео-уроков для школьников. Но поскольку такой урок это чисто человеческое взаимодействие, возникла идея прикрутить к нему немного аналитики. Такие данные могут помочь повысить конверсию, отследить эффективность уроков, замерить вовлеченность учеников и многое другое.

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

Маркеры начала и конца урока

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

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

Разбили видео на кадры

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

Научились детектировать детские улыбки (и не только)

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

Проблема 1. Распознавать лица на картинках низкого качества сложнее

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

Стандартный детектор DNN Face Detector из библиотеки OpenCV, который мы сначала взяли за основу, на наших данных давал неточные результаты. Оказалось, что алгоритм недостаточно хорошо справляется с реальными кадрами из видеочатов: иногда пропускает лица, которые явно есть в кадре, из двух лиц находил только одно или определял лица там, где их нет.

Стандартный детектор DNN Face Detector мог определить как лицо узор на занавеске, игрушечного медведя или даже композицию из картин на стене и стулаСтандартный детектор DNN Face Detector мог определить как лицо узор на занавеске, игрушечного медведя или даже композицию из картин на стене и стула

Поэтому мы решили попробовать обучить свой детектор. Для этого взяли реализацию RetinaNet-модели на PyTorch. В качестве данных для обучения подали результаты работы стандартного детектора и убедились, что новая модель учится находить лица. Затем подготовили обучающую и валидационную выборку, просматривая и при необходимости исправляя результаты работы детектора на новых кадрах: исправлять разметку работающей модели получается быстрее, чем отмечать лица на кадре с нуля.

Размечали итеративно: после добавления новой порции размеченных кадров мы заново обучали модель. А после проверки ее работы сохраняли разметку для новых кадров, наращивая обучающую выборку. Всего мы разметили 2624 кадра из 388 видеозаписей, на которых в сумме было 3325 лиц.

Таким образом удалось обучить более чувствительный в наших условиях детектор. В валидационной выборке из 140 кадров старый детектор нашел 150 лиц, а пропустил 38. Новый же пропустил только 5, а 183 обнаружил верно.

Проблема 2. В кадре присутствует не только ребенок

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

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

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

  • возраст людей на кадре с низким разрешением становится неочевидным;

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

В процессе разметки мы заметили, что очень часто родитель присутствует на уроке плечом или локтем. Так мы назвали тип кадров, когда камера направлена на ученика, но видно, что рядом сидит родитель. Обычно на таких уроках видно плечо сидящего рядом взрослого или только локоть.

На всех трех кадрах родитель присутствует, но по отдельному кадру найти его бывает непростоНа всех трех кадрах родитель присутствует, но по отдельному кадру найти его бывает непросто

Вторая модель должна была находить именно такие родительские плечи. Очевидно, что в этой задаче детектор лиц не применим, поэтому надо обучаться на кадрах целиком. Конечно, таких датасетов мы не нашли в публичном доступе и разметили около 250 000 кадров, на которых есть часть родителя, и кадры без них. Разметки на порядок больше, чем в других задачах, потому что размечать гораздо легче: можно смотреть не отдельные кадры, а отрезки видео и в несколько кликов отмечать, например, что вот эти 15 минут (900 кадров!) родитель присутствовал.

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

На верхнем графике вероятность присутствия родителя хотя бы плечом, а на нижнем вероятность того, что родитель смотрит в камеру, например, общается с преподавателемНа верхнем графике вероятность присутствия родителя хотя бы плечом, а на нижнем вероятность того, что родитель смотрит в камеру, например, общается с преподавателем

Проблема 3. Дети улыбаются по-разному

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

За основу классификатора настроения мы взяли предобученную модель ResNet34 из библиотеки fast.ai. Эту же библиотеку использовали для дообучения модели в два этапа: сначала на публичных датасетах facial_expressions и SMILEsmileD с веселыми и нейтральными лицами, потом на нашем размеченном вручную датасете с кадрами с камер учеников. Публичные датасеты решили включить, чтобы расширить размер выборки и помочь модели более качественными изображениями, чем кадры видео с планшетов и веб-камер наших учеников.

Размечали с помощью кастомного виджета. Все изображения подвергались одной и той же процедуре предобработки:

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

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

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

  4. Нормализация цветов с помощью CLAHE normalizer из библиотеки OpenCV. По ощущениям, такая нормализация лучше других вытягивает контраст на пересвеченных или темных изображениях.

Дообучаем модель для распознавания улыбок

1. Аугментации

При дообучении мы использовали достаточно жесткие аугментации:

  • Отражали изображение по горизонтали.

  • Поворачивали на случайную величину.

  • Применяли три разных искажения для изменения контраста и яркости.

  • Брали не всю картинку, а квадрат, составляющий не менее 60% от площади исходного изображения.

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

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

Пример аугментаций на одном изображении. Для наглядности аугментации сделаны до масштабирования к разрешению 64х64Пример аугментаций на одном изображении. Для наглядности аугментации сделаны до масштабирования к разрешению 64х64Код для аугментаций
# ! pip freeze | grep fastai# fastai==1.0.44import fastaiimport matplotlib.pyplot as pltfrom matplotlib import cmfrom matplotlib import colorsimport seaborn as sns%matplotlib inlinefrom pylab import rcParamsplt.style.use('seaborn-talk')rcParams['figure.figsize'] = 12, 6path = 'facial_expressions/images/'def _side_cutoff(    x,    cutoff_prob=0.25,    cutoff_intensity=(0.1, 0.25)):    if np.random.uniform() > cutoff_prob:        return x    # height and width    h, w = x.shape[1:]    h_cutoff = np.random.randint(        int(cutoff_intensity[0]*h), int(cutoff_intensity[1]*h)    )    w_cutoff = np.random.randint(        int(cutoff_intensity[0]*w), int(cutoff_intensity[1]*w)    )        cutoff_side = np.random.choice(        range(4),        p=[.34, .34, .16, .16]    ) # top, bottom, left, right.    if cutoff_side == 0:        x[:, :h_cutoff, :] = 0    elif cutoff_side == 1:        x[:, h-h_cutoff:, :] = 0    elif cutoff_side == 2:        x[:, :, :w_cutoff] = 0    elif cutoff_side == 3:        x[:, :, w-w_cutoff:] = 0    return x# side cutoff goes frist.side_cutoff = fastai.vision.TfmPixel(_side_cutoff, order=99)augmentations = fastai.vision.get_transforms(    do_flip=True,    flip_vert=False,    max_rotate=25.0,    max_zoom=1.25,    max_lighting=0.5,    max_warp=0.0,    p_affine=0.5,    p_lighting=0.5,        xtra_tfms = [side_cutoff()])def get_example():    return fastai.vision.open_image(        path+'George_W_Bush_0016.jpg',    )def plots_f(rows, cols, width, height, **kwargs):    [        get_example()        .apply_tfms(            augmentations[0], **kwargs        ).show(ax=ax)        for i,ax in enumerate(            plt.subplots(                rows,                cols,                figsize=(width,height)            )[1].flatten())    ]plots_f(3, 5, 15, 9, size=size)

2. Нормализация цвета

Мы попробовали несколько вариантов предобработки и остановились на CLAHE нормализации. Этим способом яркость и гамма выравниваются не по всему изображению, а по его частям. Результат будет приемлемый, даже если на одном изображении есть и затемненные участки, и засвеченные.

Пример нормализации цвета на изображениях из публичного датасетаПример нормализации цвета на изображениях из публичного датасетаКод для нормализации цвета
# pip freeze | grep opencv# > opencv-python==4.5.2.52import cv2import matplotlib.pyplot as pltfrom matplotlib import cmfrom matplotlib import colorsimport seaborn as sns%matplotlib inlinefrom pylab import rcParamsplt.style.use('seaborn-talk')rcParams['figure.figsize'] = 12, 6path = 'facial_expressions/images/'imgs = [    'Guillermo_Coria_0021.jpg',    'Roger_Federer_0012.jpg',]imgs = list(    map(        lambda x: path+x, imgs    ))clahe = cv2.createCLAHE(    clipLimit=2.0,    tileGridSize=(4, 4))rows_cnt = len(imgs)cols_cnt = 4imsize = 3fig, ax = plt.subplots(    rows_cnt, cols_cnt,    figsize=(cols_cnt*imsize, rows_cnt*imsize))for row_num, f in enumerate(imgs):    img = cv2.imread(f)    col_num = 0        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)    ax[row_num, col_num].imshow(img, cmap='gray')    ax[row_num, col_num].set_title('bw', fontsize=14)    col_num += 1    img_normed = cv2.normalize(        img,        None,        alpha=0,        beta=1,        norm_type=cv2.NORM_MINMAX,        dtype=cv2.CV_32F    )    ax[row_num, col_num].imshow(img_normed, cmap='gray')    ax[row_num, col_num].set_title('bw normalize', fontsize=14)    col_num += 1        img_hist_normed = cv2.equalizeHist(img)    ax[row_num, col_num].imshow(img_hist_normed, cmap='gray')    ax[row_num, col_num].set_title('bw equalizeHist', fontsize=14)    col_num += 1        img_clahe = clahe.apply(img)    ax[row_num, col_num].imshow(img_clahe, cmap='gray')    ax[row_num, col_num].set_title('bw clahe_norm', fontsize=14)    col_num += 1        for col in ax[row_num]:        col.set_xticks([])        col.set_yticks([])plt.show()

В итоге мы получили модель, способную отличить улыбку от нейтрального выражения лица в с качеством 0.93 по метрике ROC AUC. Иными словами, если взять из выборки по случайному кадру с улыбкой и без, то с вероятностью 93% модель присвоит большую вероятность улыбки кадру с улыбающимся лицом. Этот показатель мы использовали для сравнения разных вариантов дообучения и пайплайнов. Но интуитивно кажется, что это достаточно высокий уровень точности: даже человек не всегда может определить эмоцию на лице другого человека. К тому же в реальности существует гораздо больше выражений лиц кроме однозначной радости и однозначной печали.

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

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

3. Увеличение объема выборки

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

Мы несколько раз дообучили модель на публичных датасетах и подвыборках разного размера из трейна и проверили качество на валидационной выборке. На графике видно, что качество растет по мере увеличения объема подвыборки, поэтому мы разметили дополнительно еще 3 тыс. кадров: финальную модель обучали на выборке из 8500 кадров. Скорее всего и при таком объеме данных качество еще не начало выходить на плато, но перепроверять это мы не стали.

Такое упражнение называется построением learning curve, и оно в очередной раз подтверждает тезис: объем данных самое важное в модели машинного обучения.

Качество на отложенной выборке растет по мере увеличения выборки для дообученияКачество на отложенной выборке растет по мере увеличения выборки для дообучения

4. Картинки Google для обогащения выборки

Мы пробовали спарсить первые 1000 результатов картинок по запросам в духе happy, unhappy, smiling, neutral и т. д. Не ожидали получить данные высокого качества, поэтому планировали потом просмотреть их глазами и удалить совсем неподходящие. В итоге мы быстро поняли, что никакая фильтрация эти картинки не спасет, поэтому отказались от этой идеи совсем.

Примеры изображений по запросам happy и unhappyПримеры изображений по запросам happy и unhappy

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

  • есть ли в кадре лицо;

  • с какой вероятностью этот человек улыбается;

  • ребенок это или взрослый;

  • есть ли в кадре взрослый, даже если мы не нашли лица.

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

Собрали гифку

С помощью моделей мы для каждого кадра видео получили вероятности присутствия родителя или ребенка и вероятности улыбки на найденных лицах. Из этих кадров мы выбирали по 9 кадров с улыбками ребенка, которые склеивались в гифку без участия человека.

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

Примеры итоговых GIF с улыбками нашей коллеги и ее детейПримеры итоговых GIF с улыбками нашей коллеги и ее детей

Что мы в итоге получили?

Исследование и эксперимент показали, что можно быстро и без глубокой экспертизы в Computer Vision научиться различать пользователей и их эмоции по видео (даже если оно плохого качества) на основе открытых библиотек и моделей.

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

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

Статистика дисконнектов. В этом уроке был единственный дисконнект на стороне ученикаСтатистика дисконнектов. В этом уроке был единственный дисконнект на стороне ученика

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

Виджеты

Все данные мы размечали сами и делали это довольно быстро (примерно 100 кадров в минуту). В этом нам помогали самописные виджеты:

  1. Виджет для разметки кадров с улыбками.

  2. Виджет для разметки кадров с детьми и взрослыми.

  3. Виджет для разметки кадров с плечом или локтем родителя.

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

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

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

Видео работы виджета

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

Код виджета
import pandas as pdimport numpy as npimport datetimeimport randomimport osimport ipywidgets as widgetsfrom IPython.display import displayfrom pathlib import Pathclass BulkLabeler():    def __init__(self, frames_path, annotations_path,                 labels = ['0', '1'],                 predict_fn = None,                 frame_width=120,                 num_frames = 27,                 face_width = 120,                 num_faces = 27,                 myname = '?',                 ):        self.predict_fn = predict_fn        self.labels = labels        self.frames_path = frames_path        self.frame_width = frame_width        self.num_frames = num_frames        self.face_width = face_width        self.num_faces = num_faces        self.myname = myname        self.faces_batch = []                # get annotations        self.annotations_path = annotations_path        processed_videos = []        if annotations_path.exists():            annotations = pd.read_csv(annotations_path)            processed_videos = annotations.file.str.split('/').str[-3].unique()        else:            with open(self.annotations_path, 'w') as f:                f.write('file,label,by,created_at\n')                # get list of videos        self.video_ids = [x for x in os.listdir(frames_path)                           if x not in processed_videos]        random.shuffle(self.video_ids)        self.video_ind = -1                self._make_video_widgets_row()        self._make_frames_row()        self._make_range_slider()        self._make_buttons_row()        self._make_faces_row()        self._make_video_stats_row()                display(widgets.VBox([self.w_video_row,                              self.w_frames_row,                              self.w_slider_row,                              self.w_buttons_row,                              self.w_faces_row,                              self.w_faces_label,                              self.w_video_stats]))        self._on_next_video_click(0)            ### Video name and next video button        def _make_video_widgets_row(self):        # widgets for current video name and "Next video" button        self.w_current_video = widgets.Text(            value='',            description='Current video:',            disabled=False,            layout = widgets.Layout(width='500px')            )                self.w_next_video_button = widgets.Button(            description='Next video',            button_style='info', # 'success', 'info', 'warning', 'danger' or ''            tooltip='Go to the next video',            icon='right-arrow'        )                self.w_video_row = widgets.HBox([self.w_current_video, self.w_next_video_button])                self.w_current_video.observe(self._on_video_change, names='value')        self.w_next_video_button.on_click(self._on_next_video_click)                        def _on_next_video_click(self, _):        while True:            self.video_ind += 1            current_video = self.video_ids[self.video_ind]            if next(os.scandir(self.frames_path/current_video/'student_faces'), None) is not None:                break        self.w_current_video.value = current_video                    def _on_video_change(self, change):        self.video_id = change['new']        self.frame_nums_all = sorted(int(f.replace('.jpg',''))                                      for f in os.listdir(self.frames_path/self.video_id/'student_src'))        start, stop = min(self.frame_nums_all), max(self.frame_nums_all)        self.w_range_slider.min = start        self.w_range_slider.max = stop        step = self.frame_nums_all[1] - self.frame_nums_all[0] if len(self.frame_nums_all)>1 else 1        self.w_range_start.step = step        self.w_range_stop.step = step        # change to slider value will cause frames to be redrawn        self.w_range_slider.value = [start, stop]               # reset faces        self.faces_df = None        self._reset_faces_row()        self.w_video_stats.value = f'Video {self.video_id}  no annotations yet.'                def _close_video_widgets_row(self):        self.w_current_video.close()        self.w_next_video_button.close()        self.w_video_row.close()        ### Video frames box        def _make_frames_row(self):        frame_boxes = []        self.w_back_buttons = {}        self.w_forward_buttons = {}        for i in range(self.num_frames):            back_button = widgets.Button(description='<',layout=widgets.Layout(width='20px',height='20px'))            self.w_back_buttons[back_button] = i            back_button.on_click(self._on_frames_back_click)            label = widgets.Label(str(i+1), layout = widgets.Layout(width=f'{self.frame_width-50}px'))            forward_button = widgets.Button(description='>',layout=widgets.Layout(width='20px',height='20px'))            self.w_forward_buttons[forward_button] = i            forward_button.on_click(self._on_frames_forward_click)            image = widgets.Image(width=f'{self.frame_width}px')            frame_boxes.append(widgets.VBox([widgets.HBox([back_button, label, forward_button]),                                              image]))                    self.w_frames_row = widgets.GridBox(frame_boxes,                                             layout = widgets.Layout(width='100%',                                                                     display='flex',                                                                     flex_flow='row wrap'))            def _on_frames_back_click(self, button):        frame_ind = self.w_back_buttons[button]        frame = int(self.w_frames_row.children[frame_ind].children[0].children[1].value)        start, stop = self.w_range_slider.value        self.w_range_slider.value = [frame, stop]            def _on_frames_forward_click(self, button):        frame_ind = self.w_forward_buttons[button]        frame = int(self.w_frames_row.children[frame_ind].children[0].children[1].value)        start, stop = self.w_range_slider.value        self.w_range_slider.value = [start, frame]            def _close_frames_row(self):        for box in self.w_frames_row.children:            label_row, image = box.children            back, label, forward = label_row.children            image.close()            back.close()            label.close()            forward.close()            box.close()        self.w_frames_row.close()                    ### Frames range slider                        def _make_range_slider(self):        self.w_range_start = widgets.BoundedIntText(                                        value=0,                                        min=0,                                        max=30000,                                        step=1,                                        description='Frames from:',                                        disabled=False,                                        layout = widgets.Layout(width='240px')                                    )        self.w_range_stop = widgets.BoundedIntText(                                        value=30000,                                        min=0,                                        max=30000,                                        step=1,                                        description='to:',                                        disabled=False,                                        layout = widgets.Layout(width='240px')                                    )        self.w_range_slider = widgets.IntRangeSlider(            value=[0, 30000],            min=0,            max=30000,            step=1,            description='',            disabled=False,            continuous_update=False,            orientation='horizontal',            readout=True,            readout_format='d',            layout=widgets.Layout(width='500px')        )        self.w_range_flip = widgets.Button(description='Flip range',            button_style='', # 'success', 'info', 'warning', 'danger' or ''            tooltip='Invert frames selection',            layout = widgets.Layout(width=f'{self.frame_width}px'),            icon='retweet'                                          )                self.w_range_slider.observe(self._on_slider_change, names='value')        self.w_range_start.observe(self._on_range_start_change, names='value')        self.w_range_stop.observe(self._on_range_stop_change, names='value')        self.w_range_flip.on_click(self._on_range_flip)        self.w_slider_row = widgets.HBox([self.w_range_start,                                          self.w_range_slider,                                          self.w_range_stop,                                          self.w_range_flip])        def _close_range_slider(self):        self.w_range_start.close()        self.w_range_stop.close()        self.w_range_slider.close()        self.w_range_flip.close()        self.w_slider_row.close()            def _on_range_flip(self, _):        start, stop = self.w_range_slider.value        left, right = self.w_range_slider.min, self.w_range_slider.max        if start==left and right==stop:            pass        elif start - left > right - stop:            self.w_range_slider.value=[left, start]        else:            self.w_range_slider.value=[stop, right]                                                   def _on_range_start_change(self, change):        new_start = change['new']        start, stop = self.w_range_slider.value        self.w_range_slider.value = [new_start, stop]                    def _on_range_stop_change(self, change):        new_stop = change['new']        start, stop = self.w_range_slider.value        self.w_range_slider.value = [start, new_stop]                    def _on_slider_change(self, change):        start, stop = change['new']        # update text controls        self.w_range_start.max = stop        self.w_range_start.value = start        self.w_range_stop.min = start        self.w_range_stop.max = self.w_range_slider.max        self.w_range_stop.value = stop        # show frames that fit current selection        frame_nums = [i for i in self.frame_nums_all if i>=start and i<=stop]        N = len(frame_nums)        n = self.num_frames        inds = [int(((N-1)/(n-1))*i) for i in range(n)]        # load new images into image widgets        for ind, box in zip(inds, self.w_frames_row.children):            frame_num = frame_nums[ind]            filename = self.frames_path/self.video_id/'student_src'/f'{frame_num}.jpg'            with open(filename, "rb") as image:                f = image.read()            label, image = box.children            label.children[1].value = str(frame_num)            image.value = f            ### Buttons row        def _make_buttons_row(self):        labels = list(self.labels)        if self.predict_fn is not None:            labels.append('model')        self.w_default_label = widgets.ToggleButtons(options=labels,                                                      value=self.labels[0],                                                      description='Default label:')                self.w_next_batch_button = widgets.Button(description='New batch',            button_style='info', # 'success', 'info', 'warning', 'danger' or ''            tooltip='Show next batch of faces from current frame range',            icon='arrow-right'        )        self.w_save_button = widgets.Button(description='Save labels',            button_style='success', # 'success', 'info', 'warning', 'danger' or ''            tooltip='Save current labels',            icon='check'        )        self.w_buttons_row = widgets.HBox([self.w_default_label, self.w_next_batch_button, self.w_save_button])        self.w_next_batch_button.on_click(self._on_next_batch_click)        self.w_save_button.on_click(self._on_save_labels_click)                def _close_buttons_row(self):        self.w_default_label.close()        self.w_next_batch_button.close()        self.w_save_button.close()        self.w_buttons_row.close()                def _on_next_batch_click(self, _):        if self.faces_df is None:             self._create_faces_df()        # select a sample from faces_df        start, stop = self.w_range_slider.value        subdf = self.faces_df.loc[lambda df: df.frame_num.ge(start)&                                             df.frame_num.le(stop)&                                             df.label.eq('')]        num_faces = min(len(subdf), self.num_faces)                if num_faces == 0:            self.faces_batch = []            self.w_faces_label.value = 'No more unlabeled images in this frames range'            self.w_faces_label.layout.visibility = 'visible'            for box in self.w_faces_row.children:                box.layout.visibility = 'hidden'        else:            self.w_faces_label.layout.visibility = 'hidden'            self.faces_batch = subdf.sample(num_faces).index            # if we have a model then we use it to sort images            if self.predict_fn is not None:                probs, labels = self._predict()                # sort faces according to probability                ind = sorted(range(len(probs)), key=probs.__getitem__)                self.faces_batch = [self.faces_batch[i] for i in ind]                labels = [labels[i] for i in ind]            # create labels for each face            if self.w_default_label.value != 'model':                labels = [self.w_default_label.value]*len(self.faces_batch)            # update faces UI            for facefile, label, box in zip(self.faces_batch, labels, self.w_faces_row.children):                image, buttons = box.children                with open(self.frames_path/facefile, "rb") as im:                    image.value = im.read()                buttons.value = label                box.layout.visibility = 'visible'            if len(self.faces_batch) < len(self.w_faces_row.children):                for box in self.w_faces_row.children[len(self.faces_batch):]:                    box.layout.visibility = 'hidden'        def _predict(self):        probs = []        labels = []        for facefile in self.faces_batch:            prob, label = self.predict_fn(self.frames_path/facefile)            probs.append(prob)            labels.append(label)        self.faces_df.loc[self.faces_batch, 'prob'] = probs        return probs, labels                    def _on_save_labels_click(self, _):        self.w_save_button.description='Saving...'                        with open(self.annotations_path, 'a') as f:            for file, box in zip(self.faces_batch, self.w_faces_row.children):                label = box.children[1].value                self.faces_df.at[file,'label'] = label                print(file, label, self.myname, str(datetime.datetime.now()),sep=',', file=f)                # update current video statistics        stats = self.faces_df.loc[self.faces_df.label.ne(''),'label'].value_counts().sort_index()        stats_str = ', '.join(f'{label}: {count}' for label, count in stats.items())        self.w_video_stats.value = f'Video {self.video_id}  {stats_str}.'                self.w_save_button.description = 'Save labels'        # ask for next batch        self._on_next_batch_click(0)            ### Faces row        def _make_faces_row(self):        face_boxes = []        for i in range(self.num_faces):            image = widgets.Image(width=f'{self.face_width}px')            n = len(self.labels)            toggle_buttons_width = int(((self.face_width-5*(n-1))/n))            toggle_buttons = widgets.ToggleButtons(options=self.labels,                                                    value=self.w_default_label.value,                                                    style=widgets.ToggleButtonsStyle(button_width=f'{toggle_buttons_width}px'))            face_boxes.append(widgets.VBox([image, toggle_buttons]))                    self.w_faces_row = widgets.GridBox(face_boxes,                                            layout = widgets.Layout(width='100%',                                                                    display='flex',                                                                    flex_flow='row wrap'))        self.w_faces_label = widgets.Label()        self._reset_faces_row()            def _close_faces_row(self):        for box in self.w_faces_row.children:            image, buttons = box.children            for w in [image, buttons, box]:                w.close()        self.w_faces_row.close()        self.w_faces_label.close()            def _reset_faces_row(self):        for box in self.w_faces_row.children:            box.layout.visibility = 'hidden'        self.w_faces_label.layout.visibility = 'visible'        self.w_faces_label.value = 'Press "New batch" button to see a new batch of faces'        self.faces_batch = []            ### Video statistics row        def _make_video_stats_row(self):        self.w_video_stats = widgets.Label('No video currently selected')        def _close_video_stats_row(self):        self.w_video_stats.close()                def _create_faces_df(self):        folder = Path(self.video_id,'student_faces')        df = pd.DataFrame({'file':[folder/f for f in os.listdir(self.frames_path/folder)]})        df['frame_num'] = df.file.apply(lambda x: int(x.stem.split('_')[0]))        df['label'] = '' #TODO maybe existing annotations?        df['prob'] = np.nan        df = df.sort_values(by='frame_num').set_index('file')        self.faces_df = df                    def close(self):        self._close_video_widgets_row()        self._close_frames_row()        self._close_range_slider()        self._close_buttons_row()        self._close_faces_row()        self._close_video_stats_row()
Подробнее..

WebRTC CDN на Google Cloud Platform с балансировкой и автоматическим масштабированием

18.06.2021 10:22:12 | Автор: admin

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

Кратко напомним основные тезисы:

  • В CDN низкая задержка в трансляциях обеспечивается использованием технологии WebRTC для передачи видеопотока от Origin сервера к Edge серверам, которые, в свою очередь, позволяют подключить большое количество зрителей.

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

Google Cloud Platform - это стек облачных сервисов, которые выполняются на той же самой инфраструктуре, которую Google использует для своих продуктов. То есть, получается, что пользовательские приложения запущенные в среде Google Cloud Platform, крутятся на тех же серверных мощностях, что и сам "великий и ужасный" Google. Поэтому, можно, с большой вероятностью, ожидать бесперебойной работы серверной инфраструктуры.

Инфраструктура Google Cloud Platform, как и в случае с AWS, поддерживает автоматическое масштабирование и балансировку распределения нагрузки, поэтому не приходится беспокоиться о лишних расходах на оплату виртуальных серверов вы платите только за то, что используете.

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

Теперь рассмотрим, как развернуть WCS CDN с балансировщиком и автоматическим масштабированием и процесс тестирования развернутой системы.

Разворачиваем WebRTC CDN с балансировщиком и автоматическим масштабированием на Google Cloud Platform

Конфигурация CDN будет следующей:

  • один Origin сервер;

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

Для развертывания потребуется настроить следующие компоненты в консоли Google Cloud Platform:

  • глобальный файрволл на уровне проекта Google Cloud;

  • виртуальные машины WCS CDN Origin и WCS CDN Edge;

  • шаблон развертывания на основе образа диска WCS CDN Edge;

  • группу масштабирования;

  • балансировщик нагрузки.

Итак, приступим.

Настраиваем глобальный файрволл на уровне проекта Google Cloud для прохождения WebRTC трафика

Настройка межсетевого экрана действует на все запущенные в вашем проекте сервера, поэтому начнем развертывание с нее.

В основном меню консоли Google Cloud откройте раздел "VPC networks" и выберите пункт "Firewall":

На открывшейся странице нажмите кнопку "Create Firewall Rule" :

В открывшемся мастере задайте имя правила:

Ниже на странице разрешите входящий трафик с любых компьютеров:

Еще ниже в секции "Protocols and ports" укажите порты для работы WCS и нажмите кнопку "Create":

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

Разворачиваем WCS сервер с ролью Origin для WebRTC CDN

В консоли Google Cloud откройте раздел "Compute Engine" и выберите из меню в левой части пункт "VM instances". Нажмите кнопку "Create" в диалоге создания нового экземпляра сервера:

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

Ниже на странице в секции "Boot disk" нажмите кнопку "Change" и выберите образ "CentOS 7":

Разверните секцию "Management, security, disks, networking, sole tenancy":

На вкладке "Security" добавьте публичный ключ для доступа к серверу по SSH:

На вкладке "Networking" в секции "Network interfaces" настройте внешний и внутренний IP адреса для сервера. Для работы в составе CDN серверу нужно назначить статический внутренний IP адрес:

После всех настроек нажмите кнопку "Create" для создания нового экземпляра WCS сервера с ролью CDN Origin:

Спустя пару минут сервер будет создан и запущен. Подключаемся к нему по ssh и устанавливаем WCS. Все действия - установка, изменение настроек, запуск или перезапуск WCS - должны выполняться с root правами, либо через sudo.

1.Установите Wget, Midnight Commander и дополнительные инструменты и библиотеки

sudo yum -y install wget mc tcpdump iperf3 fontconfig

2.Установите JDK. Для работы в условиях больших нагрузок рекомендуется JDK 12 или 14. Удобнее провести установку при помощи скрипта на bash. Текст скрипта:

#!/bin/bashsudo rm -rf jdk*curl -s https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz | tar -zx[ ! -d jdk-12.0.2/bin ] && exit 1sudo mkdir -p /usr/java[ -d /usr/java/jdk-12.0.2 ] && sudo rm -rf /usr/java/jdk-12.0.2sudo mv -f jdk-12.0.2 /usr/java[ ! -d /usr/java/jdk-12.0.2/bin ] && exit 1sudo rm -f /usr/java/defaultsudo ln -sf /usr/java/jdk-12.0.2 /usr/java/defaultsudo update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-12.0.2/bin/java" 1sudo update-alternatives --install "/usr/bin/jstack" "jstack" "/usr/java/jdk-12.0.2/bin/jstack" 1sudo update-alternatives --install "/usr/bin/jcmd" "jcmd" "/usr/java/jdk-12.0.2/bin/jcmd" 1sudo update-alternatives --install "/usr/bin/jmap" "jmap" "/usr/java/jdk-12.0.2/bin/jmap" 1sudo update-alternatives --set "java" "/usr/java/jdk-12.0.2/bin/java"sudo update-alternatives --set "jstack" "/usr/java/jdk-12.0.2/bin/jstack"sudo update-alternatives --set "jcmd" "/usr/java/jdk-12.0.2/bin/jcmd"sudo update-alternatives --set "jmap" "/usr/java/jdk-12.0.2/bin/jmap"

3.Загрузите архив для установки самой свежей стабильной версии WebCallServer:

sudo wget https://flashphoner.com/download-wcs5.2-server.tar.gz

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

sudo tar -xvzf FlashphonerWebCallServer-5.2.714.tar.gz && cd FlashphonerWebCallServer-5.2.714 && ./install.sh

5.Для активации лицензии запустите скрипт "./activation.sh" из каталога установки WCS. Этот шаг, при желании, можно пропустить и активировать лицензию позже через веб-интерфейс:

sudo cd /usr/local/FlashphonerWebCallServer/bin && sudo ./activation.sh

6.Отключите firewalld и SELinux. Сетевой экран мы ранее настроили на уровне Google Cloud Platform, поэтому нет необходимости закрывать порты в операционной системе:

sudo systemctl stop firewalld && systemctl disable firewalld && setenforce 0

7.Откройте любым удобным редактором файл flashphoner.properties, который можно найти по пути:

/usr/local/FlashphonerWebCallServer/conf/flashphoner.properties

и внесите в него настройки для запуска CDN. В параметре "cdn_ip" укажите внутренний IP адрес вашей виртуальной машины с ролью CDN Origin:

cdn_enabled=truecdn_ip=10.128.0.3 # Local IP address CDN Origincdn_nodes_resolve_ip=falsecdn_role=origin

На скриншоте ниже примерный вид файла flashphoner.properties для WCS с ролью CDN Origin:

После изменения настроек запустите (или перезапустите) Web Call Server:

systemctl start webcallserver

На этом запуск и настройка Origin закончены. Перейдем к настройке балансировщика нагрузки и автоматического масштабирования.

Запускаем балансировщик нагрузки и автоматическое масштабирование в Google Cloud для WebRTC CDN

Для запуска балансировщика и автоматического масштабирования нужны следующие компоненты:

  • образ диска, который будет использоваться в шаблоне при создании нового экземпляра WCS;

  • шаблон, на основе которого будут создаваться новые экземпляры сервера при масштабировании;

  • группа масштабирования;

  • балансировщик нагрузки;

  • настройки контроля активности сервера.

Создаем образ диска WCS сервера с ролью Edge для WebRTC CDN

Начнем настройку с развертывания WCS сервера с ролью CDN Edge, для того, что бы создать на его основе образ диска для шаблона создания новых экземпляров WCS в балансировщике.

Повторите инструкцию по подготовке сервера Origin до пункта о внесении настроек в файл flashphoner.properties. Для роли Edge внесите в этот файл следующие настройки:

cdn_enabled=truecdn_ip=10.128.0.4cdn_nodes_resolve_ip=falsecdn_point_of_entry=10.128.0.3cdn_role=edgehttp_enable_root_redirect=false

После внесения и сохранения настроек, остановите в консоли Google Cloud виртуальную машину WCS CDN Edge, выберите из меню в левой части пункт "Images" и нажмите кнопку "Create Image":

В открывшемся мастере укажите имя нового образа, выберите в качестве источника диск виртуальной машины WCS CDN Edge и нажмите кнопку "Create":

После того, как образ диска создан переходим к созданию шаблона развертывания Edge сервера на основе созданного образа.

Создаем шаблон развертывания Edge сервера

Выберите из меню в левой части окна консоли Google Cloud пункт "Instance templates" и нажмите кнопку "Create Instance template":

В открывшемся мастере создания шаблона развертывания укажите имя шаблона и выберите конфигурацию виртуальной машины:

Ниже на странице в секции "Boot disk" нажмите кнопку "Change". в Открывшемся окне перейдите на вкладку "Custom Images" и выберите образ диска WCS CDN Edge, который мы создали ранее. Нажмите кнопку "Select":

Разверните секцию "Management, security, disks, networking, sole tenancy". На вкладке "Security" добавьте публичный ключ для доступа к серверу по SSH и нажмите кнопку "Create":

Шаблон развертывания для WCS с ролью CDN Edge создан. Теперь перейдем к созданию группы масштабирования.

Создаем группы масштабирования для Edge серверов

Из меню в левой части окна консоли Google Cloud выберите пункт "Instance groups" и нажмите кнопку "Create Instance group":

На открывшейся странице выберите регион и зону расположения группы и укажите шаблон развертывания WCS Edge, который мы создали ранее:

В секции "Autoscaling" на этой же странице настройте триггер запуска дополнительных серверов Edge. В качестве триггера будем использовать загрузку процессора более 80% . В поле "Maximum number of instances" укажите максимальное количество виртуальных машин, которые будут запущены при срабатывании триггера:

Затем включите проверку состояния виртуальной машины в секции "Autohealing". Для того, что бы создать настройку проверки сервера выберите из списка в поле "Health check" пункт "Сreate a health check":

В открывшемся мастере создания проверки состояния сервера укажите имя проверки, протокол TCP, порт 8081 и запрос /health-check. Настройте критерии проверки и нажмите кнопку "Save and continue":

Разверните секцию "Advanced creation options" и активируйте чекбокс "Do not retry machine creation". После чего нажмите "Create":

Будет создана группа масштабирования и запущен один WCS с ролью CDN Edge. Последним этапом настройки нашей CDN с балансировщиком нагрузки и автоматическим масштабированием будет настройка балансировщика.

Создаем балансировщик нагрузки

Сначала зарезервируем для балансировщика внешний IP адрес. В главном меню Google Cloud Platform в секции "VPC network" выберите пункт "External IP addresses" и нажмите кнопку "Reserve static address":

На открывшейся странице в поле "Name" задаем имя для зарезервированного IP адреса. Выбираем уровень качества сетевых услуг для адреса и тип распространения. После завершения всех настроек нажимаем кнопку "Reserve":

Затем переходим к настройке балансировщика.

Выбираем пункт "Load balancing" в разделе "Network services" секции "Networking" основного меню Google Cloud Platform:

Нажимаем кнопку "Create load balancer":

Затем выберите тип балансировщика "TCP Load Balancing" и нажмите кнопку "Start configuration":

На открывшейся странице укажите внешний балансировщик "From Internet to my VMs" и регион размещения серверов балансировщика. После выбора настроек нажмите кнопку "Continue":

На следующей странице задайте имя балансировщика, Затем перейдите в раздел настроек "Backend configuration" и укажите в каком регионе будут созданы сервера входящие в состав балансировщика. На вкладке "Select existing instance groups" выберите группу масштабирования Edge серверов, которую мы создали ранее. Затем в поле "Health check"выберите из выпадающего списка пункт "Сreate a health check":

На открывшейся странице укажите параметры для проверки состояния работы балансировщика порт 8081 и запрос /, после чего нажмите кнопку "Save and continue":

Затем перейдите к настройкам раздела "Frontend configuration". В этом разделе нужно создать привязку портов к внешнему IP адресу. Укажите внешний IP адрес для балансировщика, который мы зарезервировали выше и создайте конфигурации для TCP портов 8081, 8080, 8443, 8444 для HTTP(S) и WS(S). После создания необходимых портов нажмите кнопку "Create":

Балансировщик будет запущен. На этом развертывание CDN с балансировщиком и масштабированием можно считать завершенным. Переходим к тестированию.

Тестирование WebRTC CDN с балансировщиком и масштабированием на базе Google Cloud Platform

Методика тестирования

Для проведения нагрузочного тестирования, при создании группы масштабирования мы выставили порог загрузки процессора для срабатывания триггера на 20%. Тестирование будем проводить с использованием браузера Google Chrome и виртуальной вебкамеры для организации трансляции видеопотока. Что бы сымитировать повышение нагрузки на процессор запустим воспроизведение потока с транскодированием с помощью примера "Media Devices".

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

Тестирование

В браузере Google Chrome открываем web интерфейс WCS с ролью CDN Origin Авторизуемся, открываем пример "Two-way Streaming", устанавливаем соединение с сервером по WebSocket и публикуем видеопоток.

Затем, запускаем web интерфейс WCS CDN Edge сервера по IP адресу, который был зарезервирован при создании балансировщика.

Авторизуемся, открываем пример "Media Devices" и устанавливаем соединение с балансировщиком по WebSocket. В правом столбце настроек снимаем чек бокс "default" для параметра "Size" и задаем значения для транскодирования видеопотока. Например, если поток на Origin сервере опубликован с размерами 320х240 задаем значение 640х480. Повторите действия в нескольких вкладках браузера, для имитации большого количества зрителей.

В консоли Google Cloud видим, что были запущены две дополнительные виртуальные машины:

Для того, что бы проверить, что балансировщик распределяет соединения между активными ВМ можно использовать страницу статистики, которая доступна по адресу:

http://<WCS instance IP address>:8081/?action=stat

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

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

Хорошего стриминга!

Ссылки

Наш демо сервер

CDN для стриминга WebRTC с низкой задержкой - CDN на базе WCS

Документация по быстрому развертыванию и тестированию WCS сервера

Документация по развертыванию WCS в Google Cloud Platform

Документация по настройке балансировки нагрузки с масштабированием в GCP

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru