Статьи

Как создать приложение для обнаружения лиц с помощью React Native

Вы разработчик гибридного приложения, хотите включить распознавание лиц в свое приложение, но не знаете, с чего начать? Для начала вы можете прочитать «Введение в распознавание лиц на Android» , в котором рассказывается, как реализовать распознавание лиц на Android. Но если вы похожи на меня и не хотите писать Java-код для создания модуля React Native, который сделает это за вас, то вы попали в нужное место.

В этом руководстве мы рассмотрим API распознавания лиц , который является частью Microsoft Cognitive Services . Этот API позволяет разработчикам легко реализовывать функции обнаружения лиц в приложениях. В этом уроке я собираюсь предположить, что это не ваше первое приложение React Native. Если вы новичок в React Native, то я рекомендую вам сначала прочитать руководство по началу работы с Facebook на веб-сайте React Native. Из этого туториала вы узнаете, как настроить свою среду и создать свой первый проект React Native.

Несмотря на то, что в этом руководстве мы сосредоточимся на платформе Android, немного поработав, вы можете добавить поддержку других платформ. Убедитесь, что у вас установлена ​​Android Studio. Вы можете скачать Android Studio с портала разработчиков Google.

Прежде чем мы начнем писать наше приложение, я хотел бы немного поговорить об API, который мы будем использовать для распознавания лиц. API-интерфейс обнаружения лиц от Microsoft предоставляет функции обнаружения и распознавания лиц через облачный API-интерфейс. Это позволяет нам отправлять HTTP-запрос, содержащий изображение или URL-адрес существующего изображения в Интернете, и получать данные о любых лицах, обнаруженных на изображении.

Вы можете отправлять запросы в API обнаружения лиц Microsoft, отправив запрос POST по адресу https://api.projectoxford.ai/face/v1.0/detect . Запрос должен содержать следующую информацию заголовка:

  • Content-Type: это поле заголовка содержит тип данных тела запроса. Если вы отправляете URL-адрес изображения в Интернете, тогда значением этого поля заголовка должно быть application / json . Если вы отправляете изображение, установите поле заголовка в application / octet-stream .
  • Ключ Ocp-Apim-Subscription-Key: это поле заголовка содержит ключ API, используемый для аутентификации ваших запросов. Я покажу вам, как получить ключ API позже в этом уроке.

По умолчанию API возвращает только данные о полях, которые используются для вложения обнаруженных лиц в изображение. В оставшейся части этого урока я буду ссылаться на эти поля как на лицевые . Эту опцию можно отключить, установив для параметра запроса returnFaceRectangle значение false . Значение по умолчанию — true , что означает, что вам не нужно указывать его, если вы не хотите отключить эту опцию.

Вы можете указать несколько дополнительных параметров запроса для получения дополнительной информации об обнаруженных лицах:

  • returnFaceId : если установлено значение true , этот параметр назначает уникальный идентификатор каждому из обнаруженных лиц.
  • returnFaceLandmarks : Включив эту опцию, API возвращает массив ориентиров лица обнаруженных лиц, включая глаза, нос и губы. Эта опция по умолчанию отключена.
  • returnFaceAttributes : если эта опция включена, API ищет и возвращает уникальные атрибуты для каждого из обнаруженных лиц. Вы должны предоставить список интересующих вас атрибутов через запятую, таких как возраст, пол, улыбка, волосы на лице, поза головы и очки.

Ниже приведен пример ответа, который вы получаете от API на следующий URL-адрес запроса:

1
https://api.projectoxford.ai/face/v1.0/detect?faceId=true&faceLandmarks=true&faceAttributes=age,gender,smile,facialHair,headPose,glasses
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
[
    {
        «faceId»: «c5c24a82-6845-4031-9d5d-978df9175426»,
        «faceRectangle»: {
            «width»: 78,
            «height»: 78,
            «left»: 394,
            «top»: 54
        },
        «faceLandmarks»: {
            «pupilLeft»: {
                «x»: 412.7,
                «y»: 78.4
            },
            «pupilRight»: {
                «x»: 446.8,
                «y»: 74.2
            },
            «noseTip»: {
                «x»: 437.7,
                «y»: 92.4
            },
            «mouthLeft»: {
                «x»: 417.8,
                «y»: 114.4
            },
            «mouthRight»: {
                «x»: 451.3,
                «y»: 109.3
            },
            «eyebrowLeftOuter»: {
                «x»: 397.9,
                «y»: 78.5
            },
            «eyebrowLeftInner»: {
                «x»: 425.4,
                «y»: 70.5
            },
            «eyeLeftOuter»: {
                «x»: 406.7,
                «y»: 80.6
            },
            «eyeLeftTop»: {
                «x»: 412.2,
                «y»: 76.2
            },
            «eyeLeftBottom»: {
                «x»: 413.0,
                «y»: 80.1
            },
            «eyeLeftInner»: {
                «x»: 418.9,
                «y»: 78.0
            },
            «eyebrowRightInner»: {
                «x»: 4.8,
                «y»: 69.7
            },
            «eyebrowRightOuter»: {
                «x»: 5.5,
                «y»: 68.5
            },
            «eyeRightInner»: {
                «x»: 441.5,
                «y»: 75.0
            },
            «eyeRightTop»: {
                «x»: 446.4,
                «y»: 71.7
            },
            «eyeRightBottom»: {
                «x»: 447.0,
                «y»: 75.3
            },
            «eyeRightOuter»: {
                «x»: 451.7,
                «y»: 73.4
            },
            «noseRootLeft»: {
                «x»: 428.0,
                «y»: 77.1
            },
            «noseRootRight»: {
                «x»: 435.8,
                «y»: 75.6
            },
            «noseLeftAlarTop»: {
                «x»: 428.3,
                «y»: 89.7
            },
            «noseRightAlarTop»: {
                «x»: 442.2,
                «y»: 87.0
            },
            «noseLeftAlarOutTip»: {
                «x»: 424.3,
                «y»: 96.4
            },
            «noseRightAlarOutTip»: {
                «x»: 446.6,
                «y»: 92.5
            },
            «upperLipTop»: {
                «x»: 437.6,
                «y»: 105.9
            },
            «upperLipBottom»: {
                «x»: 437.6,
                «y»: 108.2
            },
            «underLipTop»: {
                «x»: 436.8,
                «y»: 111.4
            },
            «underLipBottom»: {
                «x»: 437.3,
                «y»: 114.5
            }
        },
        «faceAttributes»: {
            «age»: 71.0,
            «gender»: «male»,
            «smile»: 0.88,
            «facialHair»: {
                «mustache»: 0.8,
                «beard»: 0.1,
                «sideburns»: 0.02
            }
        },
        «glasses»: «sunglasses»,
        «headPose»: {
            «roll»: 2.1,
            «yaw»: 3,
            «pitch»: 0
        }
    }
]

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

Чтобы использовать API обнаружения лиц Microsoft, каждый запрос должен проходить проверку подлинности с помощью ключа API. Вот шаги, которые необходимо предпринять, чтобы получить такой ключ.

Создайте учетную запись Microsoft Live, если у вас ее еще нет. Войдите в свою учетную запись Microsoft Live и зарегистрируйте учетную запись Microsoft Azure. Если у вас еще нет учетной записи Microsoft Azure, вы можете подписаться на бесплатную пробную версию , предоставляя вам доступ к службам Microsoft в течение 30 дней.

Для API распознавания лиц это позволяет бесплатно отправлять до двадцати вызовов API в минуту. Если у вас уже есть учетная запись Azure, вы можете подписаться на план Pay-As-You-Go, чтобы платить только за то, что вы используете.

После настройки учетной записи Microsoft Azure вы будете перенаправлены на портал Microsoft Azure . На портале перейдите к панели поиска и введите когнитивные услуги в поле поиска. Нажмите на результат с надписью Cognitive Services (предварительный просмотр) Вы должны увидеть интерфейс, подобный следующему:

Счета когнитивных услуг

Нажмите кнопку « Добавить» и заполните форму, которую вам представили:

  • Имя учетной записи: введите имя, которое вы хотите присвоить ресурсу.
  • Тип API: выберите API обнаружения лица.
  • Уровень цен: для целей тестирования выберите уровень бесплатного доступа (до 20 вызовов API в минуту). Если вы хотите использовать сервис в работе, выберите другой вариант, который соответствует потребностям вашего приложения.
  • Подписка: выберите бесплатную пробную версию, если вы используете новую учетную запись Microsoft Azure. В противном случае выберите опцию Pay-As-You-Go.
  • Группа ресурсов: выберите существующую, если она у вас уже есть. В противном случае создайте новую группу ресурсов, выбрав новый параметр, и введите имя для группы ресурсов.
  • Расположение: выберите Запад США .

На следующем этапе вы должны согласиться с условиями и положениями Microsoft, чтобы продолжить. Нажмите кнопку « Создать» и дождитесь окончания развертывания ресурса.

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

Щелкните созданный ресурс и щелкните значок ключа, чтобы просмотреть ключи API, связанные с ресурсом. По умолчанию генерируются два ключа, и вы можете использовать любой из них.

Ресурс когнитивных услуг

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

Если выбрана камера, будет запущено приложение камеры по умолчанию. Если галерея выбрана, приложение позволит пользователю выбрать фотографию из галереи. После выбора фотографии кнопка обнаружения лиц становится видимой. Нажатие на эту кнопку отправит запрос в API обнаружения лиц Microsoft, который возвращает данные для обнаруженных лиц. Используя ответ API, вокруг обнаруженных лиц нарисованы маленькие прямоугольники, в том числе метки для пола и возраста человека.

Вот как будет выглядеть приложение:

Скриншот приложения распознавания лиц

Теперь мы готовы построить приложение. Давайте начнем с установки зависимостей. Откройте новое окно терминала в вашем рабочем каталоге и выполните следующую команду:

1
react native init FaceDetector

Это создает для нас новый проект React Native, который на момент написания статьи находится в версии 0.25. Когда процесс установки завершится, перейдите в папку проекта.

Далее мы устанавливаем три библиотеки, которые мы будем использовать для разработки приложения:

1
npm install lodash react-native-fetch-blob react-native-image-picker —save
  • lodash: мы используем lodash только для метода map . Мы используем этот метод для преобразования результатов, которые мы получаем от API, в компонент, который мы будем отображать.
  • response-native-image-picker : эта библиотека используется для добавления возможности выбора изображения с помощью камеры или изображения из галереи.
  • response-native-fetch-blob : эта библиотека используется для отправки сетевых запросов с содержимым BLOB-объектов. API распознавания лиц особенно нужен большой фрагмент фотографии, но API fetch не поддерживает его «из коробки», поэтому мы используем эту библиотеку, чтобы обрабатывать его для нас.

Поскольку еще не все модули React Native поддерживают React Native Package Manager , нам необходимо вручную настроить проект, чтобы модули работали без проблем. В частности, нам нужно сконфигурировать проект для правильной работы response-native-image-picker .

Внутри вашего рабочего каталога откройте файл android / settings.gradle и добавьте следующий фрагмент сразу после include ':app' :

1
2
include ‘:react-native-image-picker’
project(‘:react-native-image-picker’).projectDir = new File(settingsDir, ‘../node_modules/react-native-image-picker/android’)

Откройте файл android / app / build.gradle и найдите раздел dependencies . Это должно выглядеть примерно так:

1
2
3
4
5
dependencies
    compile fileTree(dir: «libs», include: [«*.jar»])
    compile «com.android.support:appcompat-v7:23.0.1»
    compile «com.facebook.react:react-native:+» // From node_modules
}

Добавьте следующий фрагмент в список зависимостей:

1
compile project(‘:react-native-image-picker’)

Откройте файл android / app / src / main / AndroidManifest.xml и добавьте следующий фрагмент ниже системных разрешений по умолчанию, необходимых для React Native.

1
2
3
4
<uses-permission android:name=»android.permission.CAMERA» />
<uses-permission android:name=»android.permission.WRITE_EXTERNAL_STORAGE»/>
<uses-feature android:name=»android.hardware.camera» android:required=»false»/>
<uses-feature android:name=»android.hardware.camera.autofocus» android:required=»false»/>

Для справки, системные разрешения по умолчанию:

1
2
<uses-permission android:name=»android.permission.INTERNET» />
<uses-permission android:name=»android.permission.SYSTEM_ALERT_WINDOW»/>

Откройте android / app / src / main / java / com / facedetector / MainActivity.java и добавьте следующий оператор импорта вверху класса MainActivity .

1
import com.imagepicker.ImagePickerPackage;

Я упоминал об использовании rnpm ранее. Если вы еще не установили его на свой компьютер, сейчас самое время сделать это:

1
npm install rnpm -g

После установки выполните команду rnpm link чтобы автоматически связать модули, которые вы установили в settings.gradle , build.gradle , AndroidManifest.xml и MainActivity.java .

1
rnpm link

Он заботится обо всем, что мы делали вручную, для реагирования на родственные изображения . Мы прошли ручной процесс добавления зависимости, чтобы вы знали, что делает rnpm под капотом.

Теперь мы готовы написать код. Сначала откройте index.android.js и замените содержимое этого файла следующим:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React from ‘react’;
 
import {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View
} from ‘react-native’;
 
import Detector from ‘./components/Detector’;
 
const image_picker_options = {
  title: ‘Select Photo’,
  takePhotoButtonTitle: ‘Take Photo…’,
  chooseFromLibraryButtonTitle: ‘Choose from Library…’,
  cameraType: ‘back’,
  mediaType: ‘photo’,
  maxWidth: 480,
  quality: 1,
  noData: false,
  path: ‘images’
};
 
const api_key = ‘YOUR FACE DETECTION API KEY’;
 
class FaceDetector extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Detector imagePickerOptions={image_picker_options} apiKey={api_key} />
      </View>
    );
  }
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: ‘center’,
    alignItems: ‘center’,
    backgroundColor: ‘#F5FCFF’,
  }
});
 
AppRegistry.registerComponent(‘FaceDetector’, () => FaceDetector);

То, что мы имеем выше, является стандартным кодом для файла точки входа React Native. Сначала мы импортируем необходимые нам компоненты.

01
02
03
04
05
06
07
08
09
10
11
import React from ‘react’;
 
import {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View
} from ‘react-native’;
 
import Detector from ‘./components/Detector’;

Затем мы объявляем параметры, которые понадобятся компоненту Detector . Это включает в себя параметры средства выбора изображений и ключ API, который вы получили от Microsoft Azure ранее. Не забудьте ввести свой ключ API и назначить его для api_key .

01
02
03
04
05
06
07
08
09
10
11
12
13
const image_picker_options = {
  title: ‘Select Photo’,
  takePhotoButtonTitle: ‘Take Photo…’,
  chooseFromLibraryButtonTitle: ‘Choose from Library…’,
  cameraType: ‘back’, //front or back camera?
  mediaType: ‘photo’, //the type of file that you want to pick
  maxWidth: 480, //the target width in which to resize the photo
  quality: 1, //0 to 1 for specifying the quality of the photo
  noData: false, //if set to true it disables the base64 of the file
};
 
//the API Key that you got from Microsoft Azure
const api_key = ‘YOUR FACE Detection API KEY’;

Затем мы создаем компонент точки входа и внутри основного контейнера используем компонент Detector . Не забудьте передать необходимые свойства:

1
2
3
4
5
6
7
8
9
class FaceDetector extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Detector imagePickerOptions={image_picker_options} apiKey={api_key} />
      </View>
    );
  }
}

Мы также определяем стили:

1
2
3
4
5
6
7
8
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: ‘center’,
    alignItems: ‘center’,
    backgroundColor: ‘#F5FCFF’,
  }
});

И, наконец, мы регистрируем компонент:

1
AppRegistry.registerComponent(‘FaceDetector’, () => FaceDetector);

Создайте новый файл в каталоге компонентов и назовите его Button.js . Этот компонент позволит нам легко создавать кнопки, которые выполняют определенное действие. Позже вы увидите, как это используется в компоненте Detector . А пока, знайте, что вам нужно передать onpress , button_styles , button_text_styles и text как свойства, чтобы настроить внешний вид и функциональность каждой кнопки.

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
import React from ‘react’;
 
import {
  AppRegistry,
  Component,
  Text,
  View,
  TouchableHighlight
} from ‘react-native’;
 
export default class Button extends Component {
 
  render(){
    return (
      <View>
        <TouchableHighlight underlayColor={«#E8E8E8»} onPress={this.props.onpress} style={this.props.button_styles}>
          <View>
              <Text style={this.props.button_text_styles}>{this.props.text}</Text>
          </View>
        </TouchableHighlight>
      </View>
    );
  }
}
 
AppRegistry.registerComponent(‘Button’, () => Button);

Еще в каталоге components создайте новый файл, назовите его Detector.js и добавьте в него следующий код. Этот компонент, где происходит магия. Найдите минутку, чтобы просмотреть реализацию.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import React from ‘react’;
 
import {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  Image
} from ‘react-native’;
 
 
import NativeModules, { ImagePickerManager } from ‘NativeModules’;
 
import Button from ‘./Button’;
 
import RNFetchBlob from ‘react-native-fetch-blob’;
 
import _ from ‘lodash’;
 
export default class Detector extends Component {
  constructor(props) {
      super(props);
    this.state = {
        photo_style: {
            position: ‘relative’,
            width: 480,
            height: 480
        },
        has_photo: false,
        photo: null,
        face_data: null
    };
  }
 
  render() {
    return (
      <View style={styles.container}>
         
        <Image
            style={this.state.photo_style}
            source={this.state.photo}
            resizeMode={«contain»}
        >
            { this._renderFaceBoxes .call(this) }
        </Image>
     
        <Button
            text=»Pick Photo»
            onpress={this._pickImage.bind(this)}
            button_styles={styles.button}
            button_text_styles={styles.button_text} />
 
        { this._renderDetectFacesButton.call(this) }
 
      </View>
    );
  }
 
 
  _pickImage() {
     
    this.setState({
        face_data: null
    });
 
    ImagePickerManager.showImagePicker(this.props.imagePickerOptions, (response) => {
         
      if(response.error){
        alert(‘Error getting the image. Please try again.’);
      }else{
         
        let source = {uri: response.uri};
 
        this.setState({
          photo_style: {
            position: ‘relative’,
            width: response.width,
            height: response.height
          },
          has_photo: true,
          photo: source,
          photo_data: response.data
        });
         
      }
    });
 
  }
 
  _renderDetectFacesButton() {
    if(this.state.has_photo){
        return (
            <Button
                text=»Detect Faces»
                onpress={this._detectFaces.bind(this)}
                button_styles={styles.button}
                button_text_styles={styles.button_text} />
        );
    }
  }
 
  _detectFaces() {
 
    RNFetchBlob.fetch(‘POST’, ‘https://api.projectoxford.ai/face/v1.0/detect?returnFaceId=true&returnFaceAttributes=age,gender’, {
        ‘Accept’: ‘application/json’,
        ‘Content-Type’: ‘application/octet-stream’,
        ‘Ocp-Apim-Subscription-Key’: this.props.apiKey
    }, this.state.photo_data)
    .then((res) => {
        return res.json();
    })
    .then((json) => {
         
        if(json.length){
            this.setState({
                face_data: json
            });
        }else{
            alert(«Sorry, I can’t see any faces in there.»);
        }
         
        return json;
    })
    .catch (function (error) {
        console.log(error);
        alert(‘Sorry, the request failed. Please try again.’ + JSON.stringify(error));
    });
     
 
  }
 
  _renderFaceBoxes () {
 
    if(this.state.face_data){
 
        let views = _.map(this.state.face_data, (x) => {
             
            let box = {
                position: ‘absolute’,
                top: x.faceRectangle.top,
                left: x.faceRectangle.left
            };
 
            let style = {
                width: x.faceRectangle.width,
                height: x.faceRectangle.height,
                borderWidth: 2,
                borderColor: ‘#fff’,
            };
             
            let attr = {
                color: ‘#fff’,
            };
 
            return (
                <View key={x.faceId} style={box}>
                    <View style={style}></View>
                    <Text style={attr}>{x.faceAttributes.gender}, {x.faceAttributes.age} y/o</Text>
                </View>
            );
        });
 
        return <View>{views}</View>
    }
 
  }
   
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: ‘center’,
    alignSelf: ‘center’,
    backgroundColor: ‘#ccc’
  },
  button: {
    margin: 10,
    padding: 15,
    backgroundColor: ‘#529ecc’
  },
  button_text: {
    color: ‘#FFF’,
    fontSize: 20
  }
});
 
AppRegistry.registerComponent(‘Detector’, () => Detector);

Давайте разберемся, чтобы вы точно знали, что происходит. Начнем с импорта нужных нам библиотек. Сюда входят React и его компоненты по умолчанию, средство выбора изображений, компонент button, библиотека реагировать-native-fetch-blob и lodash.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import React from ‘react’;
 
import {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  Image
} from ‘react-native’;
 
 
import NativeModules, { ImagePickerManager } from ‘NativeModules’;
 
import Button from ‘./Button’;
 
import RNFetchBlob from ‘react-native-fetch-blob’;
 
import _ from ‘lodash’;

Внутри объявления класса у нас есть конструктор, который выполняется до монтирования компонента. Здесь мы устанавливаем стиль по умолчанию для выбранной фотографии, логическое значение, которое используется в качестве основы для отображения кнопки обнаружения лиц или нет, сама фотография и face_data , которая используется в качестве источника данных для построения рамок для лица. ,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
export default class Detector extends Component {
  constructor(props) {
    super(props);
    this.state = {
        photo_style: {
            position: ‘relative’,
            width: 480,
            height: 480
        },
        has_photo: false,
        photo: null,
        face_data: null
    };
  }
   
  …
}

Далее следует метод render() , который визуализирует выбранную фотографию и две кнопки, выбирает изображение и определяет лица. Обратите внимание, что ниже мы вызываем три других метода: _renderFaceBoxes() , _pickImage() и _renderDetectedFacesButton() . Мы вскоре пройдемся по этим методам, но пока знаем, что они используются для упрощения реализации метода render() .

Также обратите внимание, что мы используем call и bind вместо прямого вызова методов. Это связано с тем, что методы внутри классов ES6 не привязываются к классу автоматически. В этом меню вам нужно либо использовать bind либо call для привязки методов к this , что относится к самому классу. Если вы не знаете разницу между bind и call , проверьте этот вопрос переполнения стека о разнице между call , apply и bind .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
render() {
  return (
    <View style={styles.container}>
   
        <Image
            style={this.state.photo_style}
            source={this.state.photo}
            resizeMode={«contain»}
          >
        { this._renderFaceBoxes.call(this) }
        </Image>
 
        <Button
          text=»Pick Photo»
          onpress={this._pickImage.bind(this)}
          button_styles={styles.button}
          button_text_styles={styles.button_text} />
 
        { this._renderDetectFacesButton.call(this) }
 
    </View>
  );
}

Метод _pickImage() вызывается при нажатии кнопки выбора изображений. Это устанавливает face_data в null чтобы существующие грани, если таковые имеются, были удалены. Затем открывается диалоговое окно выбора места для получения фотографии, камеры или галереи.

Диалог использует объект, который был передан из index.android.js, чтобы настроить его параметры. После выбора фотографии возвращается ответ, содержащий локальный URI и base64 представление фотографии, ее размеры (ширина и высота) и другую важную информацию о файле. Мы используем эти данные для обновления состояния, которое обновляет пользовательский интерфейс приложения.

Обратите внимание, что ранее мы указали maxWidth 480 для опций выбора изображений. Это означает, что размер выбранного изображения изменяется до этой ширины, а высота автоматически регулируется для сохранения соотношения сторон. Вот почему мы обновляем ширину и высоту в photo_style чтобы изменить размер компонента Image чтобы фотография photo_style по photo_style .

position устанавливается на relative так что абсолютно позиционированные грани будут ограничены внутри компонента Image . photo используется в качестве источника для компонента Image а photo_data — это представление base64 фотографии. Нам нужно поместить его в состояние, чтобы мы могли использовать его позже при отправке запроса к API.

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
28
29
_pickImage() {
   
  this.setState({
    face_data: null
  });
 
  ImagePickerManager.showImagePicker(this.props.imagePickerOptions, (response) => {
     
    if(response.error){
      alert(‘Error getting the image. Please try again.’);
    }else{
       
      let source = {uri: response.uri};
 
      this.setState({
        photo_style: {
            position: ‘relative’,
            width: response.width,
            height: response.height
        },
        has_photo: true,
        photo: source,
        photo_data: response.data
      });
       
    }
  });
 
}

Метод _renderDetectFacesButton() отвечает за отображение кнопки для обнаружения лиц. Кнопка отображается только в том случае, если для has_photo установлено значение true .

01
02
03
04
05
06
07
08
09
10
11
_renderDetectFacesButton() {
  if(this.state.has_photo){
    return (
    <Button
            text=»Detect Faces»
            onpress={this._detectFaces.bind(this)}
            button_styles={styles.button}
            button_text_styles={styles.button_text} />
    );
  }
}

При нажатии кнопки обнаружения лиц выполняется метод _detectFaces() . Этот метод отправляет запрос POST в API распознавания лиц, передавая представление base64 выбранной фотографии вместе с некоторыми параметрами в качестве параметров запроса.

Обратите внимание, что мы передаем представление base64 фотографии, но файловый блоб — это то, что фактически отправляется на сервер, потому что мы используем библиотеку реагировать-нативную-выборку-блоб . Как только мы получаем ответ, мы обновляем состояние с помощью face_data для рендеринга граней лица.

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
28
29
30
_detectFaces() {
 
  RNFetchBlob.fetch(‘POST’, ‘https://api.projectoxford.ai/face/v1.0/detect?returnFaceId=true&returnFaceAttributes=age,gender’, {
      ‘Accept’: ‘application/json’,
      ‘Content-Type’: ‘application/octet-stream’,
      ‘Ocp-Apim-Subscription-Key’: this.props.apiKey
  }, this.state.photo_data)
  .then((res) => {
    return res.json();
  })
  .then((json) => {
     
    if(json.length){
      this.setState({
        face_data: json
      });
    }else{
      //an empty array is returned if the API didn’t detect any faces
      alert(«Sorry, I can’t see any faces in there.»);
    }
     
    return json;
  })
  .catch (function (error) {
    console.log(error);
      alert(‘Sorry, the request failed. Please try again.’ + JSON.stringify(error));
  });
 
 
}

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

  • На фотографии нет лиц.
  • Лица на фотографии не распознаются алгоритмом обнаружения лиц, потому что они либо слишком большие, либо слишком маленькие, большие углы лица (поза головы), недостаточное или слишком большое освещение или что-то блокирует часть лица.

Метод _renderFaceBoxes() возвращает _renderFaceBoxes() блоки на основе face_data который в данный момент находится в state . Мы используем функцию lodash map() для циклического просмотра данных лица. Каждый блок расположен абсолютно так, что все начинается с верхнего левого края компонента Image . top и left позиции, а также width и height каждого блока основаны на значениях, хранящихся в объекте faceRectangle .

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
28
29
30
31
32
33
34
35
_renderFaceBoxes() {
 
  if(this.state.face_data){
 
    let views = _.map(this.state.face_data, (x) => {
       
      let box = {
        position: ‘absolute’,
        top: x.faceRectangle.top,
          left: x.faceRectangle.left
      };
 
      let style = {
        width: x.faceRectangle.width,
        height: x.faceRectangle.height,
        borderWidth: 2,
        borderColor: ‘#fff’,
      };
     
      let attr = {
        color: ‘#fff’,
      };
 
      return (
        <View key={x.faceId} style={box}>
            <View style={style}></View>
            <Text style={attr}>{x.faceAttributes.gender}, {x.faceAttributes.age} y/o</Text>
        </View>
      );
    });
 
    return <View>{views}</View>
  }
 
}

Прежде чем зарегистрировать компонент, мы добавляем стили.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: ‘center’,
    alignSelf: ‘center’,
    backgroundColor: ‘#ccc’
  },
  button: {
  margin: 10,
  padding: 15,
  backgroundColor: ‘#529ecc’
  },
  button_text: {
    color: ‘#FFF’,
    fontSize: 20
  }
});

И наконец, мы регистрируем компонент.

1
AppRegistry.registerComponent(‘Detector’, () => Detector);

Создайте и запустите приложение, чтобы увидеть, все ли работает правильно. Не забудьте ввести ключ API, полученный на портале Microsoft Azure. При наличии действующего ключа API приложение не сможет обнаружить лица.

Вот и все. В этом руководстве вы узнали, как создать приложение для обнаружения лиц с помощью API-интерфейса обнаружения лиц Microsoft. При этом вы узнали, как добавить службу в Microsoft Azure и сделать запрос к API обнаружения лиц.

Если вы хотите узнать больше об API обнаружения лиц, ознакомьтесь с его официальной документацией и Справочником по Cognitive Services API .