Распознавание лиц на iOS с помощью Core Image
Что мы будем делать в этом туториале?
Распознавание лиц в iOS появилось давно, еще с 5-го релиза (примерно в 2011 году), но эту особенность часто упускали из вида. API обнаружения лица позволяет разработчикам не только распознавать лица, но и проверять их на определенные свойства, такие как, наличие улыбки, зажмуривает ли человек глаза и т.д.
Во-первых, мы будем изучать технологию обнаружения лица с использованием фреймворка Core Image, создав приложение, которое распознает лицо на фотографии и обводит его специальной рамкой. Во втором примере, мы будем рассматривать более детальный способ использования, путем создания приложения, которое позволит пользователю сделать снимок, обнаружить, присутствует ли лицо, и получить координаты лица пользователя. Таким образом, мы собираемся узнать как можно больше об распознавании лиц в iOS, а также принцип использования мощного API, который так часто упускают из вида. Итак, поехали!
Настройка проекта
Загрузите стартовый проект по данной ссылке здесь и откройте его в Xcode. Как видите, очень простой контроллер с подключенным IBOutlet к ImageView.
Для того, чтобы начать обнаружение лиц использовав Core Image, вам необходимо импортировать библиотеку Core Image. Перейдите к файлу ViewController.swift и вставьте следующий код в верхней части:
import CoreImage
Обнаружение лиц, используя Core Image
В стартовом проекте мы имеем ImageView подключенный к коду в качестве IBOutlet. Далее нам нужно реализовать код для распознавания лиц. Вставьте следующий код в проект и рассмотрим его более детально:
func detect() {
guard let personciImage = CIImage(image: personPic.image!) else {
return
}
let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
let faces = faceDetector.featuresInImage(personciImage)
for face in faces as! [CIFaceFeature] {
print("Found bounds are \(face.bounds)")
let faceBox = UIView(frame: face.bounds)
faceBox.layer.borderWidth = 3
faceBox.layer.borderColor = UIColor.redColor().CGColor
faceBox.backgroundColor = UIColor.clearColor()
personPic.addSubview(faceBox)
if face.hasLeftEyePosition {
print("Left eye bounds are \(face.leftEyePosition)")
}
if face.hasRightEyePosition {
print("Right eye bounds are \(face.rightEyePosition)")
}
}
}
Итак, что здесь происходит:
- Создаем переменную personciImage, извлекаем UIImage из UIImageView и преобразуем в CIImage. CIImage необходим для работы с Core Image;
- Создаем переменную accuracy и устанавливаем CIDetectorAccuracyHigh. Вы можете выбрать из CIDetectorAccuracyHigh (обеспечивает высокую вычислительную точность) и CIDetectorAccuracyLow (использует низкую вычислительную точность). Мы выбираем CIDetectorAccuracyHigh, поскольку нам необходима высокая точность;
- Определяем переменную faceDetector и устанавливаем к классу CIDetector и передаем переменную точности, которую мы создали ранее;
- Далее получаем массив лиц, посредством вызова метода featuresInImage, детектор находит лица в данном изображении;
- Выполняем цикл по массиву лиц и преобразуем каждое распознанное лицо в CIFaceFeature;
- Создаем UIView под названием faceBox и устанавливаем его frame к размерам рамки возвращаемой из faces.first. Это необходимо для того, чтобы нарисовать прямоугольник, для выделения распознанного лица;
- Устанавливаем ширину border в faceBox до 3;
- Устанавливаем цвет границы в красный;
- Цвет фона устанавливается прозрачным, view не будет иметь видимого фона;
- И наконец, мы добавим наш faceBox к personPic;
- Мало того, что API может вам определить лицо, детектор так же может обнаружить левый и правый глаз. Мы не будем выделять глаза на изображении. Здесь я просто хочу показать вам соответствующие свойства CIFaceFeature.
Вызовем метод detect () во viewDidLoad. Так вставьте следующую строку кода в метод:
detect()
Запустим приложение. Вы увидите что-то вроде этого:
В консоле xCode мы видим координаты обнаруженного лица:
Found bounds are (177.0, 415.0, 380.0, 380.0)
Есть несколько нюансов, которые мы не рассматривали в текущем примере:
- Обнаружение лица наносится на исходное изображение, которое имеет более высокое разрешение, чем imageView. Мы установили для personPic режим to aspect fit (масштабирование изображения с сохранением исходных пропорций). Чтобы нарисовать правильно прямоугольник, мы должны рассчитать фактическое положение и размер лица в представлении изображения;
- Кроме того, Core Image и UIView (или UIKit) используют две различных системы координат (смотри рисунок ниже). Мы должны обеспечить реализацию преобразования координат Core Image в координаты UIView.
Теперь замените метод detect () следующим кодом:
func detect() {
guard let personciImage = CIImage(image: personPic.image!) else {
return
}
let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
let faces = faceDetector.featuresInImage(personciImage)
// Добавили конвертацию координат
let ciImageSize = personciImage.extent.size
var transform = CGAffineTransformMakeScale(1, -1)
transform = CGAffineTransformTranslate(transform, 0, -ciImageSize.height)
for face in faces as! [CIFaceFeature] {
print("Found bounds are \(face.bounds)")
// Добавили вычисление фактического положения faceBox
var faceViewBounds = CGRectApplyAffineTransform(face.bounds, transform)
let viewSize = personPic.bounds.size
let scale = min(viewSize.width / ciImageSize.width,
viewSize.height / ciImageSize.height)
let offsetX = (viewSize.width - ciImageSize.width * scale) / 2
let offsetY = (viewSize.height - ciImageSize.height * scale) / 2
faceViewBounds = CGRectApplyAffineTransform(faceViewBounds, CGAffineTransformMakeScale(scale, scale))
faceViewBounds.origin.x += offsetX
faceViewBounds.origin.y += offsetY
let faceBox = UIView(frame: faceViewBounds)
faceBox.layer.borderWidth = 3
faceBox.layer.borderColor = UIColor.redColor().CGColor
faceBox.backgroundColor = UIColor.clearColor()
personPic.addSubview(faceBox)
if face.hasLeftEyePosition {
print("Left eye bounds are \(face.leftEyePosition)")
}
if face.hasRightEyePosition {
print("Right eye bounds are \(face.rightEyePosition)")
}
}
}
Изменения кода выше выделены комментариями.
- Во-первых, мы используем аффинное преобразование для конвертирования координат Core Image в координаты UIKit;
- Во-вторых, мы добавили код, чтобы вычислить фактическое положение и размер нашей рамки обнаруженного лица.
Запустите приложение еще раз. Вы должны увидеть рамку вокруг лица. Поздравляю, мы успешно обнаружили лицо с помощью Core Image.
Создание приложения «камера с функцией распознавания лиц»
Давайте представим что у нас есть приложение камера, которое делает фотографии. Как только будет отснята фотография мы хотим запустить функцию распознавания лиц, чтобы определить, есть ли на фотографии лицо или нет. Если лицо присутствует, мы можем классифицировать эту фотографию с некоторыми тегами или что то в этом роде. Для этого нам нужна интеграция с классом UIImagePicker и запустить наш код распознавания лица после того, как сделан снимок.
В стартовом проекте, я уже создал класс CameraViewController. Обновите код, для реализации функций камеры:
class CameraViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet var imageView: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
}
@IBAction func takePhoto(sender: AnyObject) {
if !UIImagePickerController.isSourceTypeAvailable(.Camera) {
return
}
imagePicker.allowsEditing = false
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
imageView.contentMode = .ScaleAspectFit
imageView.image = pickedImage
}
dismissViewControllerAnimated(true, completion: nil)
self.detect()
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
}
Первые несколько строк этой функции устанавливают делегата UIImagePicker. В методе didFinishPickingMediaWithInfo (это метод делегата UIImagePicker), мы устанавливаем для imageView изображение полученное в методе. Затем мы вызываем dismiss для пикера и вызываем функцию detect.
Мы еще не реализовали функцию detect. Вставьте следующий код и давайте более внимательно взглянем на него:
func detect() {
let imageOptions = NSDictionary(object: NSNumber(int: 5) as NSNumber, forKey: CIDetectorImageOrientation as NSString)
let personciImage = CIImage(CGImage: imageView.image!.CGImage!)
let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
let faces = faceDetector.featuresInImage(personciImage, options: imageOptions as? [String : AnyObject])
if let face = faces.first as? CIFaceFeature {
print("found bounds are \(face.bounds)")
let alert = UIAlertController(title: "Say Cheese!", message: "We detected a face!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
if face.hasSmile {
print("face is smiling");
}
if face.hasLeftEyePosition {
print("Left eye bounds are \(face.leftEyePosition)")
}
if face.hasRightEyePosition {
print("Right eye bounds are \(face.rightEyePosition)")
}
} else {
let alert = UIAlertController(title: "No Face!", message: "No face was detected", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
Наша функция detect () очень похожа на ее предыдущую реализацию. Но на этот раз, мы используем ее на захваченном изображении. При обнаружении лица, мы выводим предупреждающее сообщение «We detected a face!» В противном случае, мы отображаем сообщение «No Face!». Запустите приложение и протестируйте работу.
CIFaceFeature имеет несколько свойств и методов, которые я уже упоминал. Например, если вы хотите выполнить обнаружение, если человек улыбается, вы можете вызвать .hasSmile, которое возвращает boolean. Для эксперимента вы можете вызвать .hasLeftEyePosition, чтобы проверить, присутствует ли левый глаз (будем надеяться что это так) или .hasRightEyePosition для правого глаза, соответственно.
Мы также можем вызвать hasMouthPosition, чтобы проверить присутствует ли рот. Если рот присутствует, мы можем получить доступ к координатам со свойством mouthPosition, как показано ниже:
if (face.hasMouthPosition) {
print("mouth detected")
}
Как вы видите, обнаруживать черты лица очень просто с помощью Core Image. Помимо обнаружения рта, улыбки, или положения глаза, мы можем также проверить, открыт глаз или закрыт путем вызова leftEyeClosed для левого глаза и rightEyeClosed для правого соответственно.
И в заключении
В этой статье, мы исследовали функции обнаружения лиц с помощью API, основного изображения Core Image API и как их использовать в приложении с камерой. Мы установили воспользовались основной UIImagePicker, чтобы отснять фотографию и обнаружить лицо, присутствующее на изображении.
Как вы видите, обнаружение лица с использованием основного изображения Core Image является мощным API наряду с многими приложениями! Я надеюсь, что эта статья будет вы нашли это учебник полезным и информативным руководством к для данного малоизвестного API для iOS!
» Вы можете скачать окончательный финальный проект здесь.