В помощь мобильному разработчику
Распознание лиц

Распознавание лиц на iOS

Александр Алексеев

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

Недавно я немного поиграл с вышедшей для iOS библиотекой фотогалереи и камеры под названием Chafu. iOS обеспечивает достаточно простой API-интерфейс для того, чтобы можно было выполнять массу разнообразных задач, таких как чтение кодов bar и QR, а также некоторых других доступных для чтения машиной кодов. И ещё она поддерживает распознавание лиц!

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

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

При подготовке этой статьи я исходил из того, что вы уже умеете устанавливать AVCaptureSession, AVVCaptureDeviceInput и AVCaptureVideoPreviewLayer для предпросмотра тех данных, которые выводятся из «камеры».

 

Настойка распознавания лиц

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

В установке AVCaptureMetadataOuput нет ничего сложного:

  1. Создайте этот экземпляр
  2.  Добавьте его в AVCaptureSession
  3. Узнайте, доступны ли метаданные лиц
  4. Установите обратный вызов, если в пункте 3 получено положительное подтверждение.

 

Заметили следующую вещь в обратном вызове? Это реализация IAVCaptureMetadataOutputObjectsDelegate. Я решил добавить его непосредственно в мой класс Camera. Реализация выглядит приблизительно следующим образом:

 

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

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

 

Отображение лиц

Для отображения лиц мне нравится использовать уровни иерархической структуры iOS (layers), которые позволяют нам с лёгкостью осуществлять повороты и другие преобразования. Я чуть позже объясню, как использовать Roll и Yaw из AVMetadataFaceObject при отрисовке визуального индикатора лица.

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

Теперь мы можем добавить «лица» к этому уровню в методе DidOutputMetadataObjects. И это делается всего в несколько простых шагов.

  1. Выполните итерацию массива metadataObjects, который мы получаем в качестве аргумента
  2. Убедитесь в том, что это на самом деле AVMetadataFaceObject
  3. Преобразуйте через AVCaptureVideoPreviewLayer объект GetTransformedMetadataObject, чтобы получить правильные координаты для лица
  4. Создайте для лица новый CALayer с рамкой вокруг объекта или другим желаемым эффектом
  5. Установите привязки CALayer с тем, что получилось от преобразования
  6. Добавьте это в качестве подуровня в overlayLayer, который мы создали ранее

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

Здесь я просто создаю новый CALayer и устанавливаю цвет границы, ширину и радиус скругления углов. Итак, в этом случае мы увидим белые квадраты со второй (2) шириной границы и закругленными углами. Всё просто!

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

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

 

Вызовите метод RemoveFaces(), прежде чем вы выполните итерацию metadataObjects в DidOutputMetadataObjects и всё должно получиться.

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

 

Выравнивание для углов Yaw и Roll

AVMetadataFaceObject выдает нам RollAngle при повороте шапки вокруг оси Z. Он также дает нам YawAngle для вращений вокруг оси Y.

j4wto

Поддержка RollAngle является простой, так как требуется проводить вращение не в Z плоскости, а скорее вокруг нее. Однако, вращения вокруг оси Y, будут перемещать прямоугольник в Z плоскости. По умолчанию CALayer является плоским и не имеет даже малейшей перспективы. Мы можем исправить это! Вернувшись туда, где мы создаем overlayLayer, нам нужно добавить простое преобразование, которое, получается, и даст эту перспективу.

 

Благодаря этому будет взято преобразование по умолчанию и добавлено расстояние до плоскости проекции 3D в условиях 1/z. Другими словами, мы добавили глубину. Apple делает это в обратном порядке. Следовательно, используется -1/z, где z есть расстояние, в этом случае оно 1000. Чем больше значение, тем больше расстояние. Для более подробного толкования вы можете обратиться к статье 3D-проекции на Wikipedia.

Теперь мы можем применить наши повороты к лицу CALayers.

 

Roll

Для того чтобы заставить Roll вращаться мы просто создаем новый CATransform3D, используя статический метод MakeRotation, который позволяет производить вращения вокруг любой оси. Как было показано выше roll вращается вокруг оси Z. CATransform3D ожидает того, что угол в радианах. Следовательно, нам нужно преобразовать это в первую очередь.

 

Это довольно просто. Мы применим это к уровню лица позже.

 

Yaw

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

 

Теперь нам нужно сделать преобразование Yaw и объединить его с трансформацией ориентации.

 

И, наконец, применить эти два преобразования к лицу CALayer в методе DidOutputMetadataObjects.

 

Заметьте, я добавил преобразование по умолчанию к faceLayer и сочленил roll- и/или yaw-преобразования в зависимости от их доступности. Вот и всё. Теперь у Вас должно получиться что-то вроде этого. Скриншоты взяты из Chafu, и на них демонстрируется распознавание, при котором не выполнялось вращение, а потом показаны варианты с roll и yaw.

norotrollyaw

 

 

 

 

 

 

 

 

 

 

Автор: Tomasz Cielecki (Cheesebaron)
ИсточникОфициальный блог автора
Twitter@Cheesebaron
GitHubCheesebaron

Немного об авторе:
Имеет статус  Xamarin MVP и является членом сообщества Xamarin.

Александр Алексеев
Александр Алексеев

Xamarin - разработчик. Работаю с .NET платформой с 2012 года, программирую в основном с использованием C#. За это время успел поработать с ASP.NET, Entity Framework, MSSQL, Git

Устройства iOS 10

Написать ответ