Появившись в библиотеках Vision в Play Services 8.1, функция распознавания лиц позволяет вам, как разработчику, анализировать видео или изображение для обнаружения человеческих лиц. Получив список лиц, обнаруженных на изображении, вы можете собрать информацию о каждом лице, такую как ориентация, вероятность улыбки, открытые или закрытые глаза и конкретные ориентиры на лице.
Эта информация может быть полезна для нескольких приложений, таких как приложение камеры, которое автоматически делает снимок, когда все в кадре улыбаются с открытыми глазами, или для увеличения изображений с глупыми эффектами, такими как рога единорога. Важно отметить, что распознавание лиц не распознавание лица. Хотя можно собирать информацию о лице, эта информация не используется библиотекой Vision для определения того, происходят ли два лица от одного человека.
В этом руководстве будет использоваться неподвижное изображение для запуска API обнаружения лиц и сбора информации о людях на фотографии, а также иллюстрирование этой информации с помощью наложенной графики. Весь код для этого урока можно найти на GitHub .

1. Настройка проекта
Чтобы добавить библиотеку Vision в свой проект, вам необходимо импортировать Play Services 8.1 или выше в ваш проект. Это руководство импортирует только библиотеку Play Services Vision. открыто файл build.gradle вашего проекта и добавьте следующую строку компиляции в узел dependencies .
|
1
|
compile ‘com.google.android.gms:play-services-vision:8.1.0’
|
Включив Play Services в свой проект, вы можете закрыть файл build.gradle вашего проекта и открыть AndroidManifest.xml . Вам необходимо добавить элемент meta-data определяющий зависимость лица, под узлом application вашего манифеста. Это позволяет библиотеке Vision знать, что вы планируете обнаруживать лица в вашем приложении.
|
1
|
<meta-data android:name=»com.google.android.gms.vision.DEPENDENCIES» android:value=»face»/>
|
Как только вы закончите настройку AndroidManifest.xml , вы можете закрыть его. Далее вам нужно создать новый класс с именем FaceOverlayView.java . Этот класс расширяет View и содержит логику для обнаружения лиц в проекте, отображения растрового изображения, которое было проанализировано, и рисования поверх изображения для иллюстрации точек.
А пока начнем с добавления переменных-членов в начало класса и определения конструкторов. Объект Bitmap будет использоваться для хранения растрового изображения, которое будет проанализировано, а объекты SparseArray of Face будут хранить каждое лицо, найденное в растровом изображении.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class FaceOverlayView extends View {
private Bitmap mBitmap;
private SparseArray<Face> mFaces;
public FaceOverlayView(Context context) {
this(context, null);
}
public FaceOverlayView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FaceOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
|
Затем добавьте новый метод внутри FaceOverlayView называется setBitmap(Bitmap bitmap) . На данный момент это просто сохранит переданное ему растровое изображение, однако позже вы будете использовать этот метод для анализа изображения.
|
1
2
3
|
public void setBitmap( Bitmap bitmap ) {
mBitmap = bitmap;
}
|
Далее вам нужно растровое изображение. Я включил один в пример проекта на GitHub , но вы можете использовать любое изображение, которое вам нравится, чтобы поиграть с Face Detection и посмотреть, что работает, а что нет. Когда вы выбрали изображение, поместите его в каталог res / raw . В этом уроке предполагается, что изображение называется face.jpg .
После того, как вы поместили свое изображение в каталог res / raw , откройте файл res / layout / activity_main.xml . Этот макет содержит ссылку на FaceOverlayView так что он отображается в MainActivity .
|
1
2
3
4
5
6
|
<?xml version=»1.0″ encoding=»utf-8″?>
<com.tutsplus.facedetection.FaceOverlayView
xmlns:android=»http://schemas.android.com/apk/res/android»
android:id=»@+id/face_overlay»
android:layout_width=»match_parent»
android:layout_height=»match_parent» />
|
Определив макет, откройте MainActivity и настройте FaceOverlayView из onCreate() . Это делается путем получения ссылки на представление, чтения файла изображения face.jpg из исходного каталога в качестве входного потока и преобразования его в растровое изображение. Когда у вас есть растровое изображение, вы можете вызвать setBitmap в FaceOverlayView чтобы передать изображение в ваш пользовательский вид.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class MainActivity extends AppCompatActivity {
private FaceOverlayView mFaceOverlayView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFaceOverlayView = (FaceOverlayView) findViewById( R.id.face_overlay );
InputStream stream = getResources().openRawResource( R.raw.face );
Bitmap bitmap = BitmapFactory.decodeStream(stream);
mFaceOverlayView.setBitmap(bitmap);
}
}
|
2. Обнаружение лиц
Теперь, когда ваш проект настроен, пришло время начинать распознавать лица. В setBitmap( Bitmap bitmap ) вам необходимо создать FaceDetector . Это можно сделать с помощью FaceDetector.Builder , позволяя вам определить несколько параметров, которые влияют на скорость обнаружения лиц и на то, какие другие данные будет генерировать FaceDetector .
Параметры, которые вы выбираете, зависят от того, что вы пытаетесь сделать в своем приложении. Если вы включите поиск ориентиров, то лица будут обнаруживаться медленнее. Как и в большинстве программ, у всего есть свои компромиссы. Чтобы узнать больше о параметрах, доступных для FaceDetector.Builder , вы можете найти официальную документацию на сайте разработчика Android .
|
1
2
3
4
5
|
FaceDetector detector = new FaceDetector.Builder( getContext() )
.setTrackingEnabled(false)
.setLandmarkType(FaceDetector.ALL_LANDMARKS)
.setMode(FaceDetector.FAST_MODE)
.build();
|
Вам также необходимо проверить, работает ли FaceDetector . Когда пользователь впервые использует функцию распознавания лиц на своем устройстве, Play Services необходимо выйти и получить набор небольших встроенных библиотек для обработки запроса вашего приложения. Хотя это почти всегда будет сделано до того, как ваше приложение завершит запуск, важно справиться с непредвиденной ситуацией, когда это не удалось.
Если FaceDetector работает, то вы можете преобразовать свое растровое изображение в объект Frame и передать его детектору для сбора данных о лицах на изображении. Когда вы закончите, вам нужно будет отпустить детектор, чтобы предотвратить утечку памяти. Когда вы закончите распознавать лица, вызовите invalidate() чтобы вызвать перерисовку вида.
|
1
2
3
4
5
6
7
8
|
if (!detector.isOperational()) {
//Handle contingency
} else {
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
mFaces = detector.detect(frame);
detector.release();
}
invalidate();
|
Теперь, когда вы обнаружили лица на своем изображении, пришло время использовать их. В этом примере вы просто нарисуете зеленую рамку вокруг каждого лица. Так как invalidate() был вызван после обнаружения лиц, вы можете добавить всю необходимую логику в onDraw(Canvas canvas) . Этот метод гарантирует, что растровое изображение и грани установлены, затем нарисуйте растровое изображение на холсте, а затем нарисуйте рамку вокруг каждой грани.
Поскольку разные устройства имеют разные размеры дисплея, вы также будете следить за масштабированным размером растрового изображения, чтобы на устройстве всегда было видно все изображение и все наложения отображались соответствующим образом.
|
1
2
3
4
5
6
7
8
9
|
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if ((mBitmap != null) && (mFaces != null)) {
double scale = drawBitmap(canvas);
drawFaceBox(canvas, scale);
}
}
|
Метод drawBitmap(Canvas canvas) рисует ваше растровое изображение на холсте и соответствующим образом drawBitmap(Canvas canvas) его размер, а также возвращает множитель для правильного масштабирования других измерений.
|
01
02
03
04
05
06
07
08
09
10
11
|
private double drawBitmap( Canvas canvas ) {
double viewWidth = canvas.getWidth();
double viewHeight = canvas.getHeight();
double imageWidth = mBitmap.getWidth();
double imageHeight = mBitmap.getHeight();
double scale = Math.min( viewWidth / imageWidth, viewHeight / imageHeight );
Rect destBounds = new Rect( 0, 0, (int) ( imageWidth * scale ), (int) ( imageHeight * scale ) );
canvas.drawBitmap( mBitmap, null, destBounds, null );
return scale;
}
|
Метод drawFaceBox(Canvas canvas, double scale) становится немного интереснее. Каждое лицо, которое было обнаружено и сохранено, имеет значение положения выше и слева от каждого лица. Этот метод займет эту позицию и нарисует зеленый прямоугольник, чтобы охватить каждую грань в зависимости от ее ширины и высоты.
Вам нужно определить свой объект Paint а затем SparseArray каждую Face в SparseArray чтобы найти его положение, ширину и высоту, и нарисовать прямоугольник на холсте, используя эту информацию.
|
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
|
private void drawFaceBox(Canvas canvas, double scale) {
//paint should be defined as a member variable rather than
//being created on each onDraw request, but left here for
//emphasis.
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
float left = 0;
float top = 0;
float right = 0;
float bottom = 0;
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
left = (float) ( face.getPosition().x * scale );
top = (float) ( face.getPosition().y * scale );
right = (float) scale * ( face.getPosition().x + face.getWidth() );
bottom = (float) scale * ( face.getPosition().y + face.getHeight() );
canvas.drawRect( left, top, right, bottom, paint );
}
}
|
На этом этапе вы сможете запускать свое приложение и видеть изображение с прямоугольниками вокруг каждого обнаруженного лица. Важно отметить, что API Обнаружения Лица все еще является довольно новым на момент написания этой статьи, и он может не обнаруживать каждое лицо. Вы можете поиграть с некоторыми настройками в объекте FaceDetector.Builder , чтобы надеяться собрать больше данных, хотя это не гарантировано.

3. Понимание достопримечательностей
Ориентиры — это достопримечательности на лице. API Обнаружения Лица не использует ориентиры для обнаружения лица, а скорее обнаруживает лицо целиком, прежде чем искать ориентиры. Вот почему обнаружение ориентиров является необязательным параметром, который можно включить с помощью FaceDetector.Builder .
Вы можете использовать эти ориентиры в качестве дополнительного источника информации, например, о том, где глаза субъекта, чтобы вы могли соответствующим образом реагировать в своем приложении. Есть двенадцать ориентиров , которые можно найти:
- левый и правый глаз
- левое и правое ухо
- кончик левого и правого уха
- основание носа
- левая и правая щека
- левый и правый угол рта
- основание рта
Доступные ориентиры зависят от угла обнаружения лица. Например, если кто-то смотрит в сторону, виден только один глаз, а это означает, что другой глаз не будет обнаружен. В следующей таблице указано, какие ориентиры должны быть обнаружены на основе угла Эйлера Y (направление влево или вправо) лица.
| Эйлер Y | Видимые Ориентиры |
|---|---|
| <-36 ° | левый глаз, левый рот, левое ухо, основание носа, левая щека |
| От -36 ° до -12 ° | левый рот, основание носа, нижний рот, правый глаз, левый глаз, левая щека, кончик левого уха |
| От -12 ° до 12 ° | правый глаз, левый глаз, основание носа, левая щека, правая щека, левый рот, правый рот, нижний рот |
| От 12 ° до 36 ° | правый рот, основание носа, нижний рот, левый глаз, правый глаз, правая щека, правый кончик уха |
| > 36 ° | правый глаз, правый рот, правое ухо, основание носа, правая щека |
Ориентиры также невероятно просты в использовании в вашем приложении, поскольку вы уже включили их во время распознавания лиц. Вам просто нужно вызвать getLandmarks() для объекта Face чтобы получить объекты List of Landmark которыми вы можете работать.
В этом руководстве вы будете рисовать маленький кружок на каждом обнаруженном ориентире, вызывая новый метод drawFaceLandmarks(Canvas canvas, double scale) из onDraw(canvas canvas) вместо drawFaceBox(Canvas canvas, double scale) . Этот метод берет положение каждого ориентира, корректирует его в соответствии с масштабом растрового изображения, а затем отображает круг индикатора ориентира.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
private void drawFaceLandmarks( Canvas canvas, double scale ) {
Paint paint = new Paint();
paint.setColor( Color.GREEN );
paint.setStyle( Paint.Style.STROKE );
paint.setStrokeWidth( 5 );
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
for ( Landmark landmark : face.getLandmarks() ) {
int cx = (int) ( landmark.getPosition().x * scale );
int cy = (int) ( landmark.getPosition().y * scale );
canvas.drawCircle( cx, cy, 10, paint );
}
}
}
|
После вызова этого метода вы должны увидеть маленькие зеленые кружки, закрывающие обнаруженные лица, как показано в примере ниже.

4. Дополнительные данные лица
Хотя положение лица и его ориентиры полезны, вы также можете узнать больше информации о каждом лице, обнаруженном в вашем приложении, с помощью некоторых встроенных методов объекта Face . getIsSmilingProbability() , getIsLeftEyeOpenProbability() и getIsRightEyeOpenProbability() пытаются определить, открыты ли глаза или улыбается ли обнаруженный человек, возвращая число в диапазоне от 0,0 до 1,0 . Чем ближе к 1,0, тем больше вероятность того, что человек улыбнется или у него откроется левый или правый глаз.
Вы также можете найти угол лица по осям Y и Z изображения, проверив его значения Эйлера. Значение Z Euler всегда будет сообщаться, однако вы должны использовать точный режим при обнаружении лиц, чтобы получить значение X. Вы можете увидеть пример того, как получить эти значения в следующем фрагменте кода.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private void logFaceData() {
float smilingProbability;
float leftEyeOpenProbability;
float rightEyeOpenProbability;
float eulerY;
float eulerZ;
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
smilingProbability = face.getIsSmilingProbability();
leftEyeOpenProbability = face.getIsLeftEyeOpenProbability();
rightEyeOpenProbability = face.getIsRightEyeOpenProbability();
eulerY = face.getEulerY();
eulerZ = face.getEulerZ();
Log.e( «Tuts+ Face Detection», «Smiling: » + smilingProbability );
Log.e( «Tuts+ Face Detection», «Left eye open: » + leftEyeOpenProbability );
Log.e( «Tuts+ Face Detection», «Right eye open: » + rightEyeOpenProbability );
Log.e( «Tuts+ Face Detection», «Euler Y: » + eulerY );
Log.e( «Tuts+ Face Detection», «Euler Z: » + eulerZ );
}
}
|
Вывод
В этом руководстве вы узнали об одном из основных компонентов библиотеки Play Services Vision — распознавании лиц . Теперь вы знаете, как определять лица на неподвижном изображении, как собирать информацию и находить важные ориентиры для каждого лица.
Используя то, что вы узнали, вы сможете добавить некоторые замечательные функции в свои собственные приложения для увеличения неподвижных изображений, отслеживания лиц в видеопотоке или всего, что вы можете себе представить.