В начале этого года Google представил новый продукт: Firebase Machine Learning Kit. ML Kit позволяет эффективно использовать возможности машинного обучения в Android и iOS приложениях. В этом посте я буду рассказывать о том, как с его помощью создать Android приложение для обнаружения лиц в реальном времени.
Обнаружение лиц — это только одна из возможностей компьютерного зрения, которую предлагает Firebase ML Kit (или, вернее, облегчает её использование). Это функция, которая может быть полезна во многих приложениях: подписывание людей на фотографиях, работа с селфи, добавление эмоджи и других эффектов во время съёмки, фотографирование только тогда, когда все улыбаются с открытыми глазами, и т.д. Возможности безграничны.
Мы уже публиковали статьи о других функциях Firebase ML Kit:
Однако, реализовать детектор лиц в собственном приложении по-прежнему нелегко. Нужно понять, как работает API, какую информацию он предоставляет, как её обрабатывать и использовать, учитывая ориентацию устройства, источник камеры и выбранную камеру (переднюю или заднюю).
В идеале мы должны получить код вроде этого:
camera.addFrameProcessor { frame ->
faceDetector.detectFaces(frame)
}
Основные компоненты здесь — это camera, frame, faceDetector. Прежде чем разобраться с каждым из них, предположим, что наш layout содержит сам компонент камеры и некий оверлей, на котором мы будем рисовать квадратики вокруг обнаруженных лиц.
<FrameLayout
...>
// Any other views
<CameraView
... />
<husaynhakeem.io.facedetector.FaceBoundsOverlay
... />
// Any other views
</FrameLayout>
Камера (Camera)
Независимо от того, какой API-интерфейс камеры мы используем, самое главное, чтобы он предоставлял способ обработки отдельных кадров. Таким образом, мы сможем обрабатывать каждый входящий кадр, идентифицировать лица в нём и отображать это пользователю.
Кадр (Frame)
Кадр представляет собой информацию, предоставленную камерой, для обнаружения лиц. Он должен содержать всё, что требуется детектору лиц для их обнаружения. Эта необходимая информация определяется ниже:
data class Frame(
val data: ByteArray?,
val rotation: Int,
val size: Size,
val format: Int,
val isCameraFacingBack: Boolean)
data class Size(val width: Int, val height: Int)
- data — массив байтов, содержащий информацию о том, что камера отображает;
- rotation — ориентация устройства;
- size — ширина и высота предпросмотра камеры;
- format — формат кодирования кадров;
- isCameraFacingBack — указывает, используется ли передняя камера или задняя.
Детектор лиц (Face Detector)
Детектор лиц является самым важным компонентом — он берёт кадр, обрабатывает его и затем выводит результаты пользователю. Таким образом, детектор лиц использует экземпляр FirebaseVisionFaceDetector
для обработки входящих кадров с камеры. Он также должен знать ориентацию камеры и её направление (передняя или задняя). Наконец, он должен знать на каком оверлее будут отображаться результаты. Скелет класса FaceDetector
выглядит так:
class FaceDetector(private val faceBoundsOverlay: FaceBoundsOverlay) {
private val faceBoundsOverlayHandler = FaceBoundsOverlayHandler()
private val firebaseFaceDetectorWrapper = FirebaseFaceDetectorWrapper()
fun process(frame: Frame) {
updateOverlayAttributes(frame)
detectFacesIn(frame)
}
private fun updateOverlayAttributes(frame: Frame) {
faceBoundsOverlayHandler.updateOverlayAttributes(...)
}
private fun detectFacesIn(frame: Frame) {
firebaseFaceDetectorWrapper.process(
image = convertFrameToImage(frame),
onSuccess = {
faceBoundsOverlay.updateFaces( /* Faces */)
},
onError = { /* Display error message */ })
}
}
Оверлей (Overlay)
Оверлей — это View-компонент, который находится поверх камеры. Он отображает рамки (или границы) вокруг обнаруженных лиц. Он должен знать ориентацию устройства, направление камеры (передняя или задняя) и размеры камеры (ширина и высота). Эта информация помогает определить, как рисовать границы вокруг обнаруженного лица, как масштабировать границы и следует ли их отражать.
class FaceBoundsOverlay @JvmOverloads constructor(
ctx: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : View(ctx, attrs, defStyleAttr) {
private val facesBounds: MutableList<FaceBounds> = mutableListOf()
fun updateFaces(bounds: List<FaceBounds>) {
facesBounds.clear()
facesBounds.addAll(bounds)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
facesBounds.forEach {
val centerX = /* Compute the center's x coordinate */
val centerY = /* Compute the center's ycoordinate */
drawBounds(it.box, canvas, centerX, centerY)
}
}
private fun drawBounds(box: Rect, canvas: Canvas, centerX: Float, centerY: Float) {
/* Compute the positions left, right, top and bottom */
canvas.drawRect(
left,
top,
right,
bottom,
boundsPaint)
}
}
На приведённой ниже диаграмме показаны компоненты, описанные выше, и способы взаимодействия их друг с другом с момента, когда камера подаёт на вход кадр, до момента, когда результаты отображаются пользователю.
Создание приложения для обнаружения лиц в реальном времени за 3 шага
Используя библиотеку для обнаружения лиц (которая содержит код, описанный выше), создание приложения становится довольно простым.
В этом примере я выбрал следующую библиотеку камеры.
Шаг 1. Добавьте FaceBoundsOverlay
поверх камеры.
<FrameLayout
...>
// Any other views
<CameraView
... />
<husaynhakeem.io.facedetector.FaceBoundsOverlay
... />
// Any other views
</FrameLayout>
Шаг 2. Определите экземпляр FaceDetection
и подключите его к камере.
private val faceDetector: FaceDetector by lazy {
FaceDetector(facesBoundsOverlay)
}
cameraView.addFrameProcessor {
faceDetector.process(Frame(
data = it.data,
rotation = it.rotation,
size = Size(it.size.width, it.size.height),
format = it.format))
}
Шаг 3. Настройте Firebase в проекте.
Заключение
Обнаружение лиц — это мощный функционал, а ML Kit делает его доступным и позволяет разработчикам выполнять с его помощью более сложные функции, такие как распознавание лиц, что выходит за рамки простого обнаружения: важно не только обнаружить лицо, но и определить, чьё оно.
Скоро в Ml Kit планируют добавить новую функцию — распознавание контура лица. С её помощью можно будет обнаруживать более 100 точек вокруг лица и быстро обрабатывать их. Это потенциально может быть полезно в приложениях с использованием объектов дополненной реальности или виртуальных стикеров (таких как Snapchat). Вместе функционалом обнаружения лиц можно будет создать много интересных приложений.