Появившись в библиотеках 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 — распознавании лиц . Теперь вы знаете, как определять лица на неподвижном изображении, как собирать информацию и находить важные ориентиры для каждого лица.
Используя то, что вы узнали, вы сможете добавить некоторые замечательные функции в свои собственные приложения для увеличения неподвижных изображений, отслеживания лиц в видеопотоке или всего, что вы можете себе представить.