Статьи

Создайте приложение для Android для распознавания контуров лица с помощью Firebase ML

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

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

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

Чтобы максимально использовать этот учебник, вы должны иметь доступ к следующему:

  • последняя версия Android Studio
  • устройство под управлением Android API уровня 23 или выше

Поскольку ML Kit является частью платформы Firebase, вам понадобится проект Firebase, чтобы иметь возможность использовать его в своем проекте Android Studio. Чтобы создать его, запустите Firebase Assistant, перейдя в Инструменты> Firebase .

Затем откройте раздел « Аналитика » и нажмите кнопку « Подключиться» . В появившемся диалоговом окне введите имя вашего нового проекта Firebase, выберите страну, в которой вы находитесь, и нажмите кнопку « Подключиться» .

Connect to Firebase dialog

После успешного подключения нажмите кнопку « Добавить аналитику в приложение» , чтобы помощник смог внести все необходимые изменения конфигурации Firebase в проект Android Studio.

На этом этапе, если вы откроете файл build.gradle вашего модуля app , помимо других изменений, вы должны увидеть в нем следующую зависимость implementation :

1
implementation ‘com.google.firebase:firebase-core:16.0.4’

Чтобы использовать функцию определения контура лица ML Kit, вам понадобятся еще две зависимости: одна для последней версии библиотеки ML Vision и одна для модели лица ML Vision. Вот как вы можете добавить их:

1
2
implementation ‘com.google.firebase:firebase-ml-vision:18.0.1’
implementation ‘com.google.firebase:firebase-ml-vision-face-model:17.0.2’

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

1
implementation ‘com.squareup.picasso:picasso:2.71828’

Обнаружение контура лица ML Kit всегда выполняется локально на устройстве вашего пользователя. По умолчанию модель машинного обучения, которая выполняет обнаружение контура лица, автоматически загружается при первом запуске приложения пользователем. Однако, чтобы улучшить взаимодействие с пользователем, я предлагаю вам начать загрузку, как только пользователь установит ваше приложение. Для этого добавьте следующий <meta-data> в файл AndroidManifest.xml :

1
2
3
<meta-data
   android:name=»com.google.firebase.ml.vision.DEPENDENCIES»
   android:value=»face» />

Вам понадобятся три виджета в макете вашего приложения: виджет EditText котором пользователь может ввести URL-адрес сетевой фотографии, виджет ImageView для отображения фотографии и виджет Button для запуска процесса обнаружения контура лица. Кроме того, вам понадобится виджет RelativeLayout для позиционирования трех виджетов. Поэтому добавьте следующий код в XML-файл макета вашей основной деятельности:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<RelativeLayout
        xmlns:android=»http://schemas.android.com/apk/res/android»
        android:layout_width=»match_parent»
        android:layout_height=»match_parent»
        android:padding=»16dp»>
 
    <EditText android:layout_width=»match_parent»
              android:layout_height=»wrap_content»
              android:hint=»Image URL»
              android:imeOptions=»actionGo»
              android:inputType=»textUri»
              android:id=»@+id/user_input» />
 
    <Button android:layout_width=»match_parent»
            android:layout_height=»wrap_content»
            android:text=»Detect contours»
            android:layout_alignParentBottom=»true»
            android:id=»@+id/action_button» />
 
    <ImageView android:layout_width=»match_parent»
               android:layout_height=»match_parent»
               android:id=»@+id/photo»
               android:layout_below=»@+id/user_input»
               android:layout_above=»@id/action_button»
               android:scaleType=»centerCrop»/>
 
</RelativeLayout>

С библиотекой Пикассо загрузка и отображение удаленного изображения включает вызовы только двух методов. Сначала вызов метода load() для указания URL-адреса изображения, которое вы хотите загрузить, а затем вызов метода into() для указания виджета ImageView внутри которого вы хотите отобразить загруженное изображение.

Разумеется, вы должны вызывать оба метода только после того, как пользователь завершит ввод URL-адреса. Поэтому убедитесь, что вы вызываете их внутри объекта OnEditorActionListener прикрепленного к виджету EditText вы создали на предыдущем шаге. Следующий код показывает вам, как это сделать:

1
2
3
4
5
6
7
8
9
user_input.setOnEditorActionListener { _, action, _ ->
    if(action == EditorInfo.IME_ACTION_GO) {
        Picasso.get()
            .load(user_input.text.toString())
            .into(photo)
        true
    }
    false
}

Запустите приложение и попробуйте ввести действительный URL-адрес изображения, чтобы убедиться, что оно работает правильно.

App displaying a photo

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

1
2
3
4
5
action_button.setOnClickListener {
 
    // Rest of the code goes here
     
}

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

Чтобы создать действительный объект параметров, вы должны следовать шаблону компоновщика. Поэтому создайте экземпляр класса FirebaseVisionFaceDetectorOptions.Builder , вызовите его setContourMode() и передайте ALL_CONTOURS константу ALL_CONTOURS чтобы указать, что вы хотите определить контуры всех лиц, присутствующих в ваших изображениях.

Затем вызовите метод build() для создателя объекта параметров.

1
2
3
4
5
val detectorOptions =
       FirebaseVisionFaceDetectorOptions.Builder()
           .setContourMode(
               FirebaseVisionFaceDetectorOptions.ALL_CONTOURS
           ).build()

Теперь вы можете передать объект параметров getVisionFaceDetector() класса FirebaseVision ML Kit, чтобы создать детектор контура лица.

1
2
3
val detector = FirebaseVision
                       .getInstance()
                       .getVisionFaceDetector(detectorOptions)

Детектор контура лица не может напрямую использовать фотографию, отображаемую вашим виджетом ImageView . Вместо этого он ожидает, что вы передадите ему объект FirebaseVisionImage . Чтобы создать такой объект, вы должны преобразовать фотографию в Bitmap объект. Следующий код показывает вам, как это сделать:

1
2
3
val visionImage = FirebaseVisionImage.fromBitmap(
    (photo.drawable as BitmapDrawable).bitmap
)

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

1
2
3
detector.detectInImage(visionImage).addOnSuccessListener {
    // More code here
}

Внутри слушателя по успешному выполнению вы можете использовать неявную переменную it для циклического просмотра списка обнаруженных лиц. Каждое лицо имеет большое количество точек контура, связанных с ним. Чтобы получить доступ к этим точкам, вы должны вызвать метод getContour() . Метод может возвращать точки контура нескольких различных лицевых ориентиров. Например, если вы передадите ей константу LEFT_EYE , она вернет точки, которые вам понадобятся, чтобы очертить левый глаз. Аналогичным образом, если вы передадите ему UPPER_LIP_TOP , вы получите точки, связанные с верхним краем верхней губы.

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

1
2
3
4
5
6
7
8
it.forEach {
    val contour = it.getContour(FirebaseVisionFaceContour.FACE)
    contour.points.forEach {
        println(«Point at ${it.x}, ${it.y}»)
    }
 
    // More code here
}

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

Logcat window showing coordinates of contour points

Чтобы выделить обнаруженные лица, просто нарисуем контуры вокруг них, используя точки контура. Чтобы иметь возможность рисовать такие пути, вам потребуется изменяемая копия растрового изображения вашего виджета ImageView . Создайте его, вызвав его метод copy() .

1
2
3
4
val mutableBitmap =
       (photo.drawable as BitmapDrawable).bitmap.copy(
           Bitmap.Config.ARGB_8888, true
       )

Рисование контуров путем непосредственного изменения пикселей растрового изображения может быть сложным. Поэтому создайте для него новый 2D-холст, передав его конструктору класса Canvas .

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

1
2
3
4
val canvas = Canvas(mutableBitmap)
 
val myPaint = Paint(Paint.ANTI_ALIAS_FLAG)
myPaint.color = Color.parseColor(«#99ff0000»)

Самый простой способ рисовать контуры на холсте — использовать класс Path . Используя интуитивно названные методы класса moveTo() и lineTo() , вы можете легко рисовать сложные формы на холсте.

Сейчас, чтобы нарисовать форму лица, вызовите метод moveTo() один раз и передайте ему координаты первой точки контура. Тем самым вы указываете, где начинается путь. Затем передайте координаты всех точек в метод lineTo() чтобы фактически нарисовать путь. Наконец, вызовите метод close() чтобы закрыть путь и заполнить его.

Соответственно добавьте следующий код:

1
2
3
4
5
6
val path = Path()
path.moveTo(contour.points[0].x, contour.points[0].y)
contour.points.forEach {
    path.lineTo(it.x, it.y)
}
path.close()

Чтобы отобразить путь, передайте его в метод drawPath() холста вместе с объектом Paint .

1
canvas.drawPath(path, myPaint)

И чтобы обновить виджет ImageView для отображения измененного растрового изображения, передайте растровое изображение его setImageBitmap() .

1
photo.setImageBitmap(mutableBitmap)

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

App highlighting a face

С новым API-интерфейсом обнаружения контуров лица ML Kit и небольшим творческим потенциалом вы можете легко создавать приложения на базе искусственного интеллекта, которые могут выполнять сложные задачи, связанные с компьютерным зрением, такие как обмен лицами, обнаружение эмоций или нанесение цифрового макияжа. В этом руководстве вы узнали, как использовать 2D-координаты, которые генерирует API, для рисования фигур, которые выделяют грани, присутствующие на фотографии.

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

Чтобы узнать больше об API, обратитесь к официальной документации .