Создайте целую систему домашнего наблюдения

Я недавно переехал в новый дом с охраной и системой видеонаблюдения. Ничего странного в Перу; Здания с таким уровнем безопасности довольно распространены, поскольку в стране исключительно высок уровень краж со взломом. По данным экспертов по безопасности Budget Direct, в Перу самый высокий уровень краж со взломом в мире — 2086 на 100 000 человек в год.

Тогда в голову пришла мысль: а можно ли воспроизвести такую ​​систему самостоятельно, но только для дома? Возможно, вы помните мои предыдущие статьи, где я игрался с камерами в Unity, так что у меня был некоторый опыт работы с видеопотоками, и идея выглядела осуществимой. Кроме того, я не знал, что делать с купленной веб-камерой, чтобы пройти сертификацию AWS, так что это была отличная возможность использовать ее повторно.

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

Требования

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

Основываясь на своей идее, я мог бы определить следующие требования:

  • Я должен иметь возможность визуализировать все живые видеопотоки на мониторе моего ПК.
  • Система должна работать с веб-камерой старой школы, которую мне пришлось купить, чтобы получить сертификат AWS.
  • Система должна иметь возможность обрабатывать переменное количество камер.
  • Так как я не всегда могу смотреть в монитор, то ответственные моменты (движения) надо фиксировать и хранить в онлайн-хранилище.
  • После того, как видео записано и сохранено, я должен получить уведомление на свой телефон.
  • Я должен иметь возможность загружать и визуализировать видео на своем мобильном телефоне.
  • Каждое записываемое видео должно содержать название камеры, точную дату и время.

Архитектура программного обеспечения

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

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

Unity3D — более очевидный выбор для клиентского приложения, поскольку он может работать на нескольких платформах и ОС (приложения Unity могут работать на настольных компьютерах, iOS, Android, WebGL, tvOS, PS4 и PS5).

Что касается сервера, я решил работать с Amazon Web Services (AWS) в основном из-за надежности его масштабируемых сервисов и моего опыта работы с ними.

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

Вот общая архитектура решения:

Примечания:

  • Настольное приложение будет обрабатывать видеопотоки устройств камеры и отображать их на экране.
  • Оба приложения Unity3D будут напрямую подключаться к AWS с помощью AWS SDK для .NET.
  • В этой статье я решил упростить задачу: я буду использовать пользователей IAM для безопасного подключения приложений к AWS. Если вы хотите увидеть отличную интеграцию с пользователями API Gateway и Cognito, прочитайте следующую статью: Как я построил гостиничную платформу с Unity3D и AWS.
  • Политики IAM будут ограничивать доступ к определенному сегменту S3.
  • Как только видео загружается в корзину, запускается событие S3 и вызывается функция Lambda.
  • Токен Firebase мобильного устройства хранится в таблице DynamoDB. Опять же, я сделал все просто: я вручную скопирую токен Firebase в таблицу DynamoDB, но мы также можем написать лямбда-функцию для ее автоматизации.
  • Функция Lambda вызывает службу обмена сообщениями Firebase Cloud для отправки push-уведомления на зарегистрированное мобильное устройство.

Реализация Unity3D

Отображение видеопотоков

Помните мою статью об анализе текстур в реальном времени? Мы создадим нечто подобное для настольного приложения. Нам нужны следующие объекты Unity: Camera, Plane, и WebCamTexture.

Для каждого устройства камеры мы получим поток в виде WebCamTexture и отрендерим его на Plane. Unity Camera сфокусируется на Plane, и при обнаружении движения будет записано видео.

Мы создадим объект, содержащий Plane, Text (имя камеры и дату) и запись Camera. Скрипт CamController прикреплен к родительскому объекту и будет управлять всем, что связано с видеопотоком: открытием потока, анализом текстур и записью. Для каждого устройства камеры мы клонируем (или создаем экземпляр) объект модели.

Итак, сначала мы ищем доступные устройства с веб-камерой и создадим объект для каждого из них:

Примечания:

  • Мы используем класс WebCamTexture для получения всех доступных видеоустройств.
  • Мы используем функцию Instantiate для клонирования объекта модели.

Затем мы инициализируем кулачковый контроллер:

Затем мы каждую секунду обновляем текстовое поле текущей датой и временем:

Примечания:

  • Мы используем функцию InvokeRepeating для вызова функции обновления каждую секунду.
  • Мы используем тег <mark> TextMeshPro, который предлагает действительно фантастические варианты формата, как упоминалось в документации.
  • Мы используем функцию .NET DateTime.Now для получения фактической даты и времени и функцию ToString для их форматирования.

Наконец, мы можем показать видеопотоки:

Примечания:

Вот окончательный результат с двумя камерами и моей собакой Oreo; выглядит круто 🐶 ❤️

Обнаружение движения

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

  • Объекты, движущиеся относительно быстро: люди, животные или что-то еще 👽 ходьба
  • Резкая смена освещения: в комнате включили свет, фонарики и т.п.
  • Любое сильное возмущение окружающей среды: землетрясение, пожар, взрыв, молния и т.п.

В следующем примере я сделал два одинаковых снимка подряд и проверил различия с помощью онлайн-инструмента DiffChecker. Отличия выделены розовым цветом на третьей картинке:

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

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

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

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

Это способ выполнить это в Unity3D:

Примечания:

  • Мы используем функцию GetPixel для получения массива пикселей текущего кадра.
  • Каждый цветовой канал (R, G, B) представляет собой плавающее значение в диапазоне от 0 до 1.
  • Я обнаружил, что при изменении цвета менее чем на 5% пиксель можно считать неизменным.
  • Я обнаружил, что менее 5% от общего изменения кадра можно считать неизменным.

Захват видео

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

  1. Использование класса VideoCapture: выглядит хорошо, но сейчас я использую Mac. Жаль, нужен другой вариант.
  2. Работа со сторонним активом: это лучший вариант, но я почти всегда неохотно работаю со сторонними активами. Действительно ли актив настолько хорош? Будет ли он сохраняться в будущем?
  3. Использование пакета Unity под названием Recorder. Это выглядит намного лучше, чем предыдущие варианты: разрешена мультиплатформенность, она выпущена самой Unity, а самое главное… она бесплатна!

Пакет Recorder можно установить через окно диспетчера пакетов:

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

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

Примечание: все объекты и классы видеорегистратора описаны в документации.

Подключение Unity3D к AWS

➡️ Работа с AWS SDK:

Я буду честен; Я немного боролся с этой частью и объясню, почему.

Несколько лет назад AWS предоставляла специальный SDK для Unity3D, который было очень легко установить. Он устарел и теперь включен в AWS SDK для .NET. Звучит неплохо, но в документации AWS нет подробностей об использовании этого SDK в проекте Unity.

Для .NET Microsoft использует механизм NuGet. В Visual Studio вы можете открыть окно NuGet Packages, в котором вы можете визуализировать пакеты .NET, установленные в вашем текущем проекте.

Для текущего проекта нам нужно реализовать интеграцию с S3, поэтому нам нужен базовый пакет AWSSDK.Core и пакет S3 AWSSDK.S3.

Но держись! Окно NuGet в основном предназначено для проектов .NET, поэтому ваш проект Unity не сможет распознать пакеты, если вы установите их через окно NuGet; вам нужно сделать это вручную с веб-сайта NuGet.

На веб-сайте NuGet вы можете загрузить основной пакет AWS и пакет AWS S3. Поскольку функции AWS SDK используют асинхронные задачи, необходимо также загрузить пакет AsyncInterfaces.

После загрузки вы можете разархивировать пакеты и поместить их в папку Plugins вашего проекта Unity:

Новые пакеты теперь распознаются внутри проекта Unity; мы сделали это хорошо 🙌

➡️ Функции:

Загрузить файл:

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

Примечания:

  • Мы создаем S3-клиент благодаря классам AmazonS3Client и AmazonS3Config.
  • Строим запрос благодаря классу PutObjectRequest.
  • Класс Task — это чистый .NET-механизм для управления асинхронными процессами. Вы можете вызвать его в Unity с помощью оператора await внутри асинхронной функции.

Список сегментов:

В клиентском приложении нам нужно вывести содержимое корзины S3. Благодаря методу ListObjectsV2 мы получим список имен файлов.

Примечания:

  • Строим запрос благодаря классу ListObjectV2Request.
  • Мы сохраняем ответ в списке имен файлов.

Скачать файл:

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

Примечания:

  • Строим запрос благодаря классу GetObjectRequest.
  • Мы используем persistentDataPath для хранения загруженного нами файла на устройстве.

Показать видео

Мы будем использовать VideoPlayercomponent для показа видео внутри клиентского приложения и выберем «URL» в качестве источника.

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

Примечания:

  • Сначала мы проверяем, присутствует ли файл уже в устройстве с помощью метода File.Exists. Если нет, то скачиваем.

Firebase

В консоли Firebase создайте новый проект, а затем новое приложение Unity.

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

Для этого проекта мы установим пакет FirebaseAnalytics (как рекомендуется) и пакет FirebaseMessaging.

В клиентском приложении Unity мы можем использовать функцию GetTokenAsync для получения токена устройства Firebase:

Реализация AWS

S3

Создаем приватный репозиторий, где будут храниться все видеофайлы. Я назову свой «мониторинг-аб».

Я

В IAM мы создадим двух пользователей: одного для загрузки видеофайлов на S3, а другого для их загрузки. В консоли IAM создадим двух пользователей с программным доступом:

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

Эти учетные данные будут использоваться в Unity при создании клиента S3:

Затем нам нужно создать политику для каждого пользователя. Политики позволят пользователю выполнять действия с определенной корзиной (в моем случае — с мониторингом). Для этого проекта удобнее создавать встроенные политики, а не управляемые политики, поскольку они будут неотъемлемой частью каждого пользователя и не будут использоваться повторно. Вы можете проверить Документацию IAM для более подробной информации.

Поэтому мы создадим встроенную политику для каждого пользователя. Для этого нажмите кнопку «Добавить встроенную политику» на экране сведений о пользователе.

Для загружаемого пользователя мы создаем политику с разрешениями ListBucket и GetObject для конкретного ведра Monitoring-ab.

Для пользователя загрузки мы создаем политику с разрешением PutObject для конкретного Bucket Monitoring-ab.

ДинамоДБ

В консоли DynamoDB создайте новую таблицу с appId в качестве ключа раздела:

Затем создайте элемент, связанный с именем корзины S3, и массив с токенами устройств Firebase:

лямбда

➡️ Лямбда-слой:

Прежде чем писать функцию Lambda, мы должны загрузить Firebase Python library в слое Lambda. Библиотека работает с Python ›=3.7, поэтому мы будем работать с самой последней версией Python, поддерживаемой Lambda, Python 3.9. Мы могли бы загрузить библиотеку вместе с кодом, но, внедрив ее в слой, мы могли бы повторно использовать библиотеку Firebase в других проектах и ​​сосредоточиться на коде.

Отличный совет по созданию уровня Lambda — сначала установить необходимые библиотеки в новой локальной среде. Я рекомендую использовать Сообщество PyCharm, которое интегрирует локальные виртуальные среды. Единственное, что вам нужно сделать, это создать новый проект вместе с новой связанной виртуальной средой.

Итак, в новой среде Python 3.9 мы сначала обновим PIP, диспетчер пакетов Python, а затем установим библиотеку Firebase благодаря следующим командам в терминале PyCharm:

После установки вы можете визуализировать библиотеку Firebase и все ее зависимости в папке venv вашего проекта:

Выглядит хорошо! Следующий шаг — скопировать папку lib, содержащуюся в папке venv, в новую папку с именем «python» и, наконец, заархивировать ее.

Мы почти закончили! Теперь мы войдем в консоль AWS, создадим новый слой Lambda для Python 3.9 и загрузим zip-файл.

Наш слой готов к использованию!

➡️ Лямбда-функция:

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

Прежде всего, мы создадим лямбда-функцию для Python 3.9.

В созданной новой функции Lambda не забудьте добавить слой firebase:

Затем мы возвращаемся к консоли S3 и создаем событие S3, чтобы новая функция Lambda запускалась после загрузки каждого файла в корзину. Функция Lambda будет вызываться после обнаружения события «Put» в корзине:

Вернитесь к Лямбде; мы видим, что событие S3 учтено, и слой добавлен:

И вот функция:

Примечания:

  • Чтобы получить учетные данные вашего проекта Firebase, вам сначала нужно создать закрытый ключ в формате JSON. Это легко сделать в настройках проекта, как указано в документации firebase-admin. Затем вы можете загрузить учетные данные в виде локального файла в свою функцию Lambda.

Конечный результат

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

В настольном приложении мы видим, как Орео и Симба идут ко мне из спальни:

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

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

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

Расходы

Это обновление! После публикации этой статьи Сет Уэллс сделал отличное замечание в разделе комментариев: как насчет затрат? Давайте проделаем упражнение с самой пессимистичной гипотезой.

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

Исходя из этого значения, предполагая, что на камерах всегда есть движение, у нас будет 24x60/5 = 288 видео в день, около 9000 видео в месяц. Размер видеофайла может варьироваться в зависимости от желаемого разрешения. Давайте поговорим о видео размером 1 МБ в течение 10 секунд. Теперь давайте проверим стоимость с помощью Калькулятора цен AWS.

  • IAM: IAM можно использовать бесплатно.
  • S3: Это основная служба нашей системы. Имея 9 ГБ данных в месяц, 9000 запросов на размещение, 1000 запросов на списки (гипотетически, когда пользователь открывает клиентское приложение) и 1000 запросов на получение (гипотетически, когда пользователь загружает видео), мы получаем счет в размере 0,26. долларов США в месяц, 3,12 долларов США в год.
  • Lambda: 9 000 запросов в месяц, среднее время выполнения запроса – 3 секунды и 128 МБ выделенной памяти – наш счет составляет 0,00 доллара США, хорошие новости!
  • DynamoDB: DynamoDB здесь используется недостаточно: мы не пишем ни в одну таблицу, и у нас есть запрос на чтение каждые 5 минут. Стоимость: 0,00 долларов США.
  • Firebase: Firebase Cloud Messaging абсолютно бесплатен.

Всего: 0,26 доллара США в месяц, 3,12 доллара США в год при пессимистическом прогнозе. Можно смело сказать, что наша система доступна по цене!

Заключительные мысли

В этой статье у нас была возможность увидеть, как построить целую облачную архитектуру и как вызвать из нее внешнюю службу обмена сообщениями (Firebase). Мы также смогли создать два приложения Unity3D, которые напрямую взаимодействуют с AWS, благодаря .NET SDK. Кроме того, мы могли оценить стоимость всей системы благодаря калькулятору AWS.

Каждый код в этой статье был протестирован с использованием Unity 2021.3.3 и Visual Studio Community 2022 для Mac. Мобильное устройство, которое я использовал для запуска приложения Unity, — это Galaxy Tab A7 Lite с Android 11.

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

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

Особая благодарность Gianca Chavest за создание потрясающей иллюстрации.