В этом уроке я буду использовать Microsoft Face API для создания приложения распознавания лиц с помощью React Native. Я собираюсь предположить, что вы уже создали приложение React Native, поэтому оно не охватывает все части кода. Если вы новичок в React Native, я рекомендую вам прочитать мой предыдущий учебник « Создание приложения для Android с React Native ». Вы можете найти полный исходный код приложения для этого урока на Github .
Получение ключа API
Первым шагом является получение ключа API от Microsoft Azure. Если вы не знаете, что такое Microsoft Azure, это платформа облачных вычислений Microsoft. Зарегистрируйте учетную запись Microsoft Live, если у вас ее еще нет. Для новичков в Azure подпишитесь на бесплатную пробную версию . Это даст вам 30 дней на бесплатное использование Azure при условии, что вы не превысите выделенные HTTP-запросы для бесплатного уровня API-интерфейса Face. Если вы уже создали учетную запись ранее, вы можете использовать существующий тарифный план. Если у вас его нет, выберите план Pay-As-You-Go, чтобы вам платили только за то, что вы используете.
После того, как вы зарегистрировали учетную запись, войдите в систему и щелкните значок поиска в верхней части экрана и выполните поиск «когнитивных услуг». В раскрывающемся списке нажмите на учетные записи Cognitive Services (предварительный просмотр), и вы должны увидеть следующий экран:
Нажмите на кнопку « Добавить», и появится следующий экран:
Заполните обязательные поля. Вы можете ввести любое имя для учетной записи, и подписка должна быть «бесплатной пробной», если вы новый пользователь, и «оплачивать по ходу», если вы уже являетесь пользователем. Ресурсная группа может быть любым именем, которое вы хотите. Расположение группы ресурсов должно быть сервером рядом с вашим текущим местоположением, но вы можете придерживаться значения по умолчанию, если хотите. В качестве типа API выберите «Face API», а затем «свободный» для уровня ценообразования . Это гарантирует, что вы ничего не тратите. Когда вы закончите заполнять все, нажмите на кнопку «Создать» и дождитесь окончания развертывания ресурса.
Как только ресурс будет развернут, нажмите на меню « Все ресурсы», расположенное на левой боковой панели. Созданный вами ресурс должен быть указан там, иначе обновите страницу. Нажмите на ресурс, и вы должны увидеть следующий экран:
Нажмите на клавиши для просмотра ключей API. Запомните один из этих ключей, так как вы будете использовать его позже при запросе к API.
Сборка приложения
Приложение, которое вы собираетесь создать, будет выполнять следующие действия:
- Создание списка лиц. Список лиц — это контейнер для лиц. Думайте об этом как о базе данных для группы связанных лиц.
- Добавление лиц в список лиц. Список лиц — это контейнер, необходимо добавить фотографии, содержащие одно лицо. Они будут использованы для сравнения позже.
- Получить похожее лицо : используется для получения сходного лица из списка лиц на основе лица, обнаруженного на фотографии.
Сначала необходимо создать список лиц, добавить к нему фотографии лиц, а затем получить список похожих лиц на основе предоставленной фотографии.
Вот как будет выглядеть приложение, когда пользователь загрузит фотографию и нажмет кнопку для получения похожего лица:
Установка зависимостей
Сначала создайте новый проект React Native:
react-native init RNSimilar
Примечание. В этом руководстве я использую React Native версии 0.25.1. Проект React Native движется быстро, поэтому, если на момент написания этой статьи уже была доступна более высокая версия, существует вероятность того, что используемые зависимости могут сломаться для этой версии React Native. Если вы читаете это позже, я рекомендую rninit , альтернативу инструменту CLI React Native, который позволяет вам установить конкретную версию React Native. Вы можете установить его глобально, используя следующую команду:
npm install -g rninit
После установки вы можете создать новый проект React Native с помощью следующей команды:
rninit init RNSimilar --source [email protected]
Это установит React Native версии 0.25.1 и React версии 0.14.8.
Перейдите в папку RNS Similar , откройте package.json, и вы должны увидеть следующее:
{ "name": "RNSimilar", "version": "0.0.1", "private": true, "scripts": { "start": "react-native start" }, "dependencies": { "react": "^0.14.8", "react-native": "^0.25.1" } }
Добавьте следующий текст справа ниже react-native
:
"react-native-button": "^1.6.0", "react-native-fetch-blob": "^0.4.0", "react-native-image-picker": "^0.18.15"
Запустите npm install
чтобы установить только что добавленные модули.
Вы используете зависимость реагировать на собственные кнопки для создания кнопок, реагируете на выборку изображений для выбора изображений из фотогалереи пользователей (или камеры устройства) и реагируете на выборку для отправки большого двоичного объекта выбранного изображения. к API.
Затем установите глобальный менеджер пакетов React Native :
npm install rnpm -g
После установки, пока вы все еще находитесь в корневом каталоге проекта, запустите rnpm link
чтобы автоматически настроить проект для добавления необходимых разрешений Android и связать файлы для работы сторонних модулей.
Файл точки входа
Откройте index.android.js и замените код по умолчанию следующим:
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, View } from 'react-native'; import SimilarFaces from './components/SimilarFaces'; const image_picker_options = { title: 'Select Photo', takePhotoButtonTitle: 'Take Photo...', chooseFromLibraryButtonTitle: 'Choose from Library...', cameraType: 'back', mediaType: 'photo', maxWidth: 480, quality: 1, noData: false, }; //the API Key that you got from Microsoft Azure const api_key = 'YOUR FACE API KEY'; class RNSimilar extends Component { render() { return ( <View style={styles.container}> <SimilarFaces imagePickerOptions={image_picker_options} apiKey={api_key} /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#ccc', } }); AppRegistry.registerComponent('RNSimilar', () => RNSimilar);
Разбивка кода выше. Сначала вы импортируете компонент SimilarFaces
, где вы найдете большую часть логики приложения.
import SimilarFaces from './components/SimilarFaces';
Объявите параметры, которые будет использовать средство выбора изображений. Это в основном понятно, поэтому я не буду их здесь объяснять. Вы можете посетить репозиторий Github этого компонента, чтобы увидеть, какие другие варианты вы можете предоставить.
const image_picker_options = { title: 'Select Photo', takePhotoButtonTitle: 'Take Photo...', chooseFromLibraryButtonTitle: 'Choose from Library...', cameraType: 'back', mediaType: 'photo', maxWidth: 480, quality: 1, noData: false, };
Добавьте ключ API из ранее:
const api_key = 'YOUR FACE API KEY';
Внутри основного компонента приложения используйте компонент SimilarFaces
который вы вскоре создадите. Передайте параметры выбора изображения и ключ API в качестве свойств, чтобы вы могли получить к ним доступ внутри компонента.
class RNSimilar extends Component { render() { return ( <View style={styles.container}> <SimilarFaces imagePickerOptions={image_picker_options} apiKey={api_key} /> </View> ); } }
Компоненты схожих лиц
Создайте файл component / SimilarFaces.js и добавьте следующее:
import { AppRegistry, StyleSheet, Text, View, Image, TextInput, ScrollView } from 'react-native'; import React, { Component } from 'react'; import NativeModules, { ImagePickerManager } from 'NativeModules'; import Button from 'react-native-button'; import Requestor from '../lib/Requestor'; let facelist_id = 'facelist_005'; let facelist_data = { name: 'My 5th facelist' }; let face_api_base_url = 'https://api.projectoxford.ai'; export default class SimilarFaces extends Component { constructor(props) { super(props); this.state = { name: '', photo_style: { width: 480, height: 480 }, photo: null, similar_photo: null, message: '' }; } render() { return ( <ScrollView> <View style={styles.container}> <Button containerStyle={styles.button} onPress={this._createFaceList.bind(this)}> Create Face List </Button> <Image style={this.state.photo_style} source={this.state.photo} resizeMode={"contain"} /> <Button containerStyle={styles.button} onPress={this._pickImage.bind(this)}> Pick Image </Button> <TextInput style={styles.text_input} onChangeText={this._changeName.bind(this)} value={this.state.name} placeholder={"name"} /> <Button containerStyle={styles.button} onPress={this._addFaceToFaceList.bind(this)}> Add Face to Face List </Button> <Button containerStyle={styles.button} onPress={this._getSimilarFace.bind(this)}> Get Similar Face </Button> <Image style={this.state.photo_style} source={this.state.similar_photo} resizeMode={"contain"} /> <Text style={styles.message}>{this.state.message}</Text> </View> </ScrollView> ); } _changeName(text) { this.setState({ name: text }); } _pickImage() { 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: { width: response.width, height: response.height }, photo: source, photo_data: response.data }); } }); } _createFaceList() { Requestor.request( face_api_base_url + '/face/v1.0/facelists/' + facelist_id, 'PUT', this.props.apiKey, JSON.stringify(facelist_data) ) .then(function(res){ alert('Face List Created!'); }); } _addFaceToFaceList() { var user_data = { name: this.state.name, filename: this.state.photo.uri }; Requestor.upload( face_api_base_url + '/face/v1.0/facelists/' + facelist_id + '/persistedFaces', this.props.apiKey, this.state.photo_data, { userData: JSON.stringify(user_data) } ) .then((res) => { alert('Face was added to face list!'); }); } _getSimilarFace() { Requestor.upload( face_api_base_url + '/face/v1.0/detect', this.props.apiKey, this.state.photo_data ) .then((facedetect_res) => { let face_id = facedetect_res[0].faceId; let data = { faceId: face_id, faceListId: facelist_id, maxNumOfCandidatesReturned: 2 } Requestor.request( face_api_base_url + '/face/v1.0/findsimilars', 'POST', this.props.apiKey, JSON.stringify(data) ) .then((similarfaces_res) => { let similar_face = similarfaces_res[1]; Requestor.request( face_api_base_url + '/face/v1.0/facelists/' + facelist_id, 'GET', this.props.apiKey ) .then((facelist_res) => { let user_data = {}; facelist_res['persistedFaces'].forEach((face) => { if(face.persistedFaceId == similar_face.persistedFaceId){ user_data = JSON.parse(face.userData); } }); this.setState({ similar_photo: {uri: user_data.filename}, message: 'Similar to: ' + user_data.name + ' with confidence of ' + similar_face.confidence }); }); }); }); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center' }, button: { padding: 10, margin: 20, height: 45, overflow: 'hidden', backgroundColor: 'white' }, text_input: { height: 40, borderColor: 'gray', borderWidth: 1, backgroundColor: '#FFF' }, message: { fontSize: 20, fontWeight: 'bold' } }); AppRegistry.registerComponent('SimilarFaces', () => SimilarFaces);
Разбивка кода выше. Сначала у вас есть обычные операторы импорта:
import { AppRegistry, StyleSheet, Text, View, Image, TextInput, ScrollView } from 'react-native';
Включите ответную нативную кнопку и ответную нативную сборку изображений :
import NativeModules, { ImagePickerManager } from 'NativeModules'; import Button from 'react-native-button';
Включите пользовательскую библиотеку для выполнения HTTP-запросов к Face API. Вы создадите это в разделе Requestor
позже.
import Requestor from '../lib/Requestor';
Объявите данные, используемые при создании списка лиц, добавлении лиц в список лиц и получении аналогичного лица. facelist_id
— это уникальный идентификатор списка лиц, а facelist_data
— это описательное имя, которое вы хотите дать списку лиц. Вы можете изменить значения для них, если хотите.
let facelist_id = 'facelist_003'; let facelist_data = { name: 'My facelist' };
Добавьте базовые URL для Face API:
let face_api_base_url = 'https://api.projectoxford.ai';
Добавьте значения по умолчанию для данных, которые вы будете использовать внутри компонента:
constructor(props) { super(props); this.state = { name: '', //the name of the person to add photo_style: { //default styling for the selected photo position: 'relative', width: 480, height: 480 }, photo: null, //the source uri of the selected photo similar_photo: null, //the source uri of the similar photo message: '' //the message to display once a similar face is returned }; }
Метод render
возвращает пользовательский интерфейс приложения. Он содержит кнопку для создания списка лиц, отображает фотографию, выбранную пользователем, кнопку для выбора фотографий, текстовый ввод для ввода имени добавленного лица, кнопку для получения похожего лица, фотографию похожего лица и пользовательское сообщение.
render() { return ( <ScrollView> <View style={styles.container}> <Button containerStyle={styles.button} onPress={this._createFaceList.bind(this)}> Create Face List </Button> <Image style={this.state.photo_style} source={this.state.photo} resizeMode={"contain"} /> <Button containerStyle={styles.button} onPress={this._pickImage.bind(this)}> Pick Image </Button> <TextInput style={styles.text_input} onChangeText={this._changeName.bind(this)} value={this.state.name} placeholder={"name"} /> <Button containerStyle={styles.button} onPress={this._addFaceToFaceList.bind(this)}> Add Face to Face List </Button> <Button containerStyle={styles.button} onPress={this._getSimilarFace.bind(this)}> Get Similar Face </Button> <Image style={this.state.photo_style} source={this.state.similar_photo} resizeMode={"contain"} /> <Text style={styles.message}>{this.state.message}</Text> </View> </ScrollView> ); }
Функция _changeName
обновляет состояние нового значения текстового поля для ввода имени человека.
_changeName(text) { this.setState({ name: text }); }
Функция _pickImage
позволяет пользователю выбирать изображение с камеры или из галереи. Для этого используется библиотека реагировать на родственные изображения . Получив ответ, обновите стиль контейнера изображения, указав размеры выбранного изображения. Также установите путь к файлу и представление base64 изображения в состояние. Таким образом, вы можете использовать его позже при добавлении лиц в список лиц или при получении подобного лица.
_pickImage() { 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: { width: response.width, height: response.height }, photo: source, //file path of the image photo_data: response.data //base64 representation of the image }); } }); }
Далее _createFaceList
функция _createFaceList
которая отправляет запрос на создание нового списка лиц в Face API. facelist_data
— это жестко закодированный объект, который вы объявили ранее в верхней части файла.
_createFaceList() { Requestor.request( face_api_base_url + '/face/v1.0/facelists/' + facelist_id, 'PUT', this.props.apiKey, JSON.stringify(facelist_data) ) .then(function(res){ alert('Face List Created!'); }); }
Функция _addFaceToFaceList
добавляет данные лица в список лиц , на этот раз используя функцию upload
из библиотеки Requestor. Принимается URL, на который вы хотите отправить запрос в качестве первого аргумента, ключ API в качестве второго, представление base64 выбранной фотографии в качестве третьего и пользовательские данные в качестве четвертого. Значение userData
преобразуется в строку, поскольку функция upload
отправляет его в качестве параметра запроса. Вот почему вы ограничены отправкой минимальных данных с максимальной длиной 1 КБ.
_addFaceToFaceList() { var user_data = { name: this.state.name, filename: this.state.photo.uri }; Requestor.upload( face_api_base_url + '/face/v1.0/facelists/' + facelist_id + '/persistedFaces', this.props.apiKey, this.state.photo_data, { userData: JSON.stringify(user_data) } ) .then((res) => { alert('Face was added to face list!'); }); }
Функция _getSimilarFace
сначала отправляет запрос конечной точке обнаружения лица . Это назначает уникальный идентификатор лицам, обнаруженным на фотографии, и возвращает их вместе с шириной, высотой, верхом и левым положением для создания рамки, окружающей обнаруженное лицо. Но для этого урока вы используете только идентификатор лица.
_getSimilarFace() { Requestor.upload( face_api_base_url + '/face/v1.0/detect', this.props.apiKey, this.state.photo_data ) .then((facedetect_res) => { ... }); }
Вот пример ответа, чтобы дать вам представление о том, как выглядят данные, с которыми вы работаете:
[ { "faceId": "c5c24a82-6845-4031-9d5d-978df9175426", "faceRectangle": { "width": 78, "height": 78, "left": 394, "top": 54 } } ]
Как только вы получите ответ, извлеките faceId
из первой строки данных. В приведенном ниже коде предполагается, что фотография имеет одно лицо, поэтому она выделяет только первый ряд.
let face_id = facedetect_res[0].faceId;
Затем создайте объект, содержащий данные, необходимые для конечной точки поиска похожих граней . Это включает в себя face_id
полученный от вызова распознавания лиц, идентификатор списка лиц, в котором API-интерфейс Face будет выполнять поиск, и максимальное количество похожих лиц, которые нужно вернуть. В этом случае придерживаться двух.
Вы указываете 2
хотя вы собираетесь отображать только одно похожее лицо, потому что указание 1
вернет фотографию с самым близким соответствием. Это означает, что если вы добавите me.jpg и me2.jpg в список лиц, а затем снова выберите me.jpg для получения похожих граней, он вернет me.jpg, если вы укажете только 1
в качестве значения для maxNumOfCandidatesReturned
.
Если вы укажете 2
а затем извлечете вторую строку из результатов, вы получите me2.jpg . Это работает наоборот (подача me2.jpg в конечную точку схожих граней возвращает me.jpg ). Все это означает, что вы получаете второе наиболее близкое соответствие, поэтому нет никаких шансов, что API когда-либо вернет одну и ту же фотографию.
let data = { faceId: face_id, faceListId: facelist_id, maxNumOfCandidatesReturned: 2 }
Отправьте запрос в конечную точку поиска похожих лиц, преобразовав данные, построенные в строку, потому что это то, что ожидается от API Face.
Requestor.request( face_api_base_url + '/face/v1.0/findsimilars', 'POST', this.props.apiKey, JSON.stringify(data) //convert data to a string ) .then((similarfaces_res) => { ... });
Получив ответ, извлеките вторую строку из полученных результатов. Затем сделайте запрос к конечной точке для получения определенного списка лиц . Он содержит все данные лица, добавленные пользователем. Пролистайте его, чтобы найти тот, у кого тот же идентификатор лица, что и у конечной точки схожих лиц.
Примечание . Вам необходимо проанализировать userData
поскольку они сохраняются в виде строки. Затем вы используете данные, чтобы обновить состояние с подобной фотографией и сообщением, содержащим имя подобного лица и номер доверия. Доверительное число находится в диапазоне от 0 до 1. Числа ближе к 1 означают более высокую вероятность того, что предоставленная пользователем фотография того же человека, что и фотография, возвращенная API.
let similar_face = similarfaces_res[1]; Requestor.request( face_api_base_url + '/face/v1.0/facelists/' + facelist_id, 'GET', this.props.apiKey ) .then((facelist_res) => { let user_data = {}; facelist_res['persistedFaces'].forEach((face) => { if(face.persistedFaceId == similar_face.persistedFaceId){ user_data = JSON.parse(face.userData); } }); this.setState({ similar_photo: {uri: user_data.filename}, message: 'Similar to: ' + user_data.name + ' with confidence of ' + similar_face.confidence }); });
Вот пример ответа от конечной точки поиска похожих лиц:
[ { "persistedFaceId" : "015839fb-fbd9-4f79-ace9-7675fc2f1dd9", "confidence" : 0.82 }, ... ]
Добавьте стили:
const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center' }, button: { padding: 10, margin: 20, height: 45, overflow: 'hidden', backgroundColor: 'white' }, text_input: { height: 40, borderColor: 'gray', borderWidth: 1, backgroundColor: '#FFF' }, message: { fontSize: 30, fontWeight: 'bold' } });
Requestor
Библиотека Requestor отправляет HTTP-запросы в Face API. Создайте файл lib / Requestor.js и добавьте следующее:
import RNFetchBlob from 'react-native-fetch-blob'; module.exports = { upload: function(url, api_key, photo, query_params){ var ret = []; for(var d in query_params){ ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(query_params[d])); } var url = url + "?" + ret.join("&"); return RNFetchBlob.fetch('POST', url, { 'Accept': 'application/json', 'Content-Type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key': api_key }, photo) .then((res) => { return res.json(); }) .then((json) => { return json; }) .catch(function (error) { console.log(error); }); }, request: function(url, method, api_key, data){ let headers = { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': api_key }; let options = { 'method': method, 'headers': headers }; if(typeof data != 'undefined'){ options.body = data; } return fetch(url, options) .then((res) => { return res.json(); }) .then((json) => { return json; }) .catch(function(error){ console.log(error); }); } }
Разбивка кода выше. Функция upload
обрабатывает все HTTP-запросы на загрузку файлов в Face API. При этом используется библиотека реагировать-родным-извлечением-блобом для отправки представления фотографии в base64 вместе с информацией заголовка, необходимой для API Face.
upload: function(url, api_key, photo, query_params){ if(typeof query_params != 'undefined'){ //construct the query parameter from the query_params object let ret = []; for(let d in query_params){ ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(query_params[d])); } let url = url + "?" + ret.join("&"); //combine the query parameters with the URL } return RNFetchBlob.fetch('POST', url, { 'Accept': 'application/json', 'Content-Type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key': api_key }, photo) .then((res) => { return res.json(); }) .then((json) => { return json; }) .catch(function (error) { console.log(error); }); },
Далее идет метод request
который обрабатывает простые HTTP-запросы, такие как отправка строковых данных на сервер.
request: function(url, method, api_key, data){ let headers = { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': api_key }; let options = { 'method': method, 'headers': headers }; if(typeof data != 'undefined'){ options.body = data; } return fetch(url, options) .then((res) => { return res.json(); }) .then((json) => { return json; }) .catch(function(error){ console.log(error); }); }
Играя с приложением
Запустите приложение. Первое, что вам нужно сделать, это загрузить фотографии лиц из Интернета. Убедитесь, что каждая фотография содержит одно лицо и что у каждого должно быть как минимум две фотографии.
После загрузки фотографий скопируйте их на свое устройство и запустите приложение.
Нажмите на кнопку создания списка лиц . Вы должны увидеть предупреждение о том, что список лиц создан. Затем нажмите на кнопку выбора изображения и выберите одну из фотографий. Введите имя человека и нажмите на кнопку добавления в список лиц . Вы должны увидеть предупреждение о том, что лицо было добавлено. Делайте это столько раз, сколько хотите, но убедитесь, что у каждого человека есть как минимум две фотографии.
Наконец, снова нажмите кнопку выбора изображения и выберите фотографию, которую хотите использовать для получения похожего лица. После выбора нажмите на кнопку получить похожее лицо . Это должно вернуть одну из фотографий этого человека, которую вы добавили ранее, но не ту, которую вы выбрали.
Смотреть в будущее
Это оно! В этом уроке вы работали с Microsoft Face API. Специально освещая функцию распознавания лиц и поиска похожих лиц. Есть и другие интересные вещи, которые вы можете сделать с Face API, вот другие функции, на которые стоит обратить внимание:
- Обнаружение лица : это обязательное условие для получения подобного лица, поэтому я не рассмотрел такие функции обнаружения лица, как оценка возраста человека или угадывание его пола. Вы также можете определить черты лица, например, если человек носит очки или нет, или волосы на лице.
- Группировка лиц : для группировки лиц на основе сходства.
- Проверка лица : для проверки подлинности двух обнаруженных лиц. Вы можете использовать это как форму аутентификации в своих приложениях React Native. Вместо того, чтобы пользователь вводил свою электронную почту и пароль, он может сделать селфи для входа в систему.
- Идентификация лица : для идентификации людей по обнаруженному лицу. Как и API схожих лиц, вы должны сначала добавить грани, прежде чем сможете его использовать.
Любые другие комментарии или вопросы, пожалуйста, дайте мне знать ниже .