Dans l'article précédent, j'ai décrit comment déployer le modèle de reconnaissance faciale de Python sur Heroku.
Déployez le modèle de reconnaissance faciale Python sur Heroku et utilisez-le depuis Flutter ①
Cette fois, je voudrais vous présenter comment appeler ce modèle à partir d'une application mobile et réaliser une comparaison de visages. Flutter a été utilisé pour créer un échantillon de l'application mobile.
Nous tweetons sur le développement d'applications qui utilisent la reconnaissance faciale sur Twitter. https://twitter.com/studiothere2
Le journal de développement des applications est sérialisé en note. https://note.com/there2
Il s'agit d'une application simple qui juge si deux images sont la même personne en sélectionnant deux images dans la galerie et en appuyant sur le bouton de comparaison en bas à droite. Obtenez l'incorporation de chaque image à partir du service WEB créé dans l'article précédent et calculez la norme L2 entre les embeddings. Si la norme L2 est de 0,6 ou moins, il est considéré comme étant la même personne, et si elle est supérieure à cela, il est considéré comme une autre personne.
Par exemple, si vous sélectionnez une personne différente comme indiqué ci-dessous, la norme L2 sera de 0,6 ou plus et elle sera jugée comme une personne différente.
Si la norme L2 est de 0,5 ou moins, il y a une forte possibilité qu'il s'agisse de la même personne et le seuil pour juger s'il s'agit de la même personne avec une précision d'environ 99% est de 0,6.
pubsec.yaml
dependencies:
flutter:
sdk: flutter
image_picker: ^0.6.6+1
ml_linalg: ^12.7.1
http: ^0.12.1
http_parser: ^3.1.4
-Obtenir une image de la galerie avec ʻimage_picker. C'est un excellent moyen d'obtenir des images de la galerie ou de l'appareil photo. --La norme L2 est calculée avec
ml_linalg`. Il s'agit d'une bibliothèque Dart qui peut effectuer des opérations vectorielles.
http
, http_parser
.La première est la partie importation. Importation des bibliothèques requises.
main.dart
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:ml_linalg/linalg.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import './secret.dart';
secret.dart
contient des informations pour accéder aux services WEB. Ceci n'est pas inclus dans git
, alors veuillez lire comme il convient
main.dart
void main() => runApp(MyApp());`
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
Jusqu'à présent, rien n'a été changé avec les valeurs par défaut créées pour le nouveau projet. Voici les classes principales.
main.dart
class _MyHomePageState extends State<MyHomePage> {
///Image de comparaison 1
Uint8List _cmpImage1;
///Image de comparaison 2
Uint8List _cmpImage2;
//Distance euclidienne entre deux faces.
double _distance = 0;
void initState() {
super.initState();
}
Il déclare un membre qui contient les données d'octets (ʻUint8List) des deux images à comparer en tant que variables membres, et une variable
_distance` qui contient la norme L2 (distance euclidienne) d'incorporation des deux images.
main.dart
Future<Uint8List> _readImage() async {
var imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
return imageFile.readAsBytesSync();
}
Utilisez la bibliothèque ʻImagePickerpour obtenir des images de la galerie. La valeur de retour sera de type
File, donc convertissez-la au format ʻUint8List
en utilisant la méthode readAsBytesSync ()
.
ʻUint8List est un tableau de type ʻint
et peut être traité comme des données d'octets.
main.dart
///Renvoie la similitude en fonction de la distance euclidienne entre les deux images
String _getCompareResultString() {
if (_distance == 0) {
return "";
} else if (_distance < 0) {
return "En traitement....";
} else if (_distance < 0.6) {
return "Même personne";
} else {
return "Une autre personne";
}
}
Cette fonction renvoie le résultat de la comparaison d'images dans le texte selon la valeur de _distance
dans la norme L2.
-1 traite et renvoie le texte sous la forme de la même personne s'il est égal ou inférieur à 0,6, et comme personne différente s'il est supérieur à 0,6. Appelez-le à partir du widget et affichez-le à l'écran.
C'est un peu long, alors je vais le diviser et le regarder dans l'ordre.
main.dart
void uploadFile() async {
setState(() {
_distance = -1;
});
var response;
var postUri = Uri.parse(yourRemoteUrl);
var request1 = http.MultipartRequest("POST", postUri);
Tout d'abord, définissez _distance
sur -1 pour que l'écran montre qu'il est en cours de traitement.
Veuillez lire postUri
séparément.
Nous préparons une requête http ici.
main.dart
//Premier fichier
debugPrint("start: " + DateTime.now().toIso8601String());
request1.files.add(http.MultipartFile.fromBytes('file', _cmpImage1.toList(),
filename: "upload.jpeg ", contentType: MediaType('image', 'jpeg')));
response = await request1.send();
if (response.statusCode == 200) print("Uploaded1!");
L'image obtenue à partir de la galerie est définie dans la requête http et la requête est envoyée. Si la valeur de retour est «200», cela réussit.
main.dart
var featureString1 = await response.stream.bytesToString();
List<double> embeddings1 =
(jsonDecode(featureString1) as List<dynamic>).cast<double>();
debugPrint("end: " + DateTime.now().toIso8601String());
La valeur de retour du service Web est convertie d'un tableau d'octets en chaîne de caractères et obtenue (featureString1
), jsonDecode
est convertie en double
, et par conséquent, elle est acquise sous forme de tableau de type double
. ..
Il s'agit de l'incorporation de l'image, et vous pouvez juger s'il s'agit de la même personne en les comparant.
main.dart
//Deuxième fichier
var request2 = http.MultipartRequest("POST", postUri);
request2.files.add(http.MultipartFile.fromBytes('file', _cmpImage2.toList(),
filename: "upload.jpeg ", contentType: MediaType('image', 'jpeg')));
response = await request2.send();
if (response.statusCode == 200) print("Uploaded2!");
var featureString2 = await response.stream.bytesToString();
List<double> embeddings2 =
(jsonDecode(featureString2) as List<dynamic>).cast<double>();
Jusqu'à présent, j'ai fait la même chose pour la deuxième image. Vient ensuite la partie calcul de la norme L2.
main.dart
var distance = Vector.fromList(embeddings1)
.distanceTo(Vector.fromList(embeddings2), distance: Distance.euclidean);
setState(() {
_distance = distance;
});
}
C'est très simple car il utilise la bibliothèque ml_linalg
.
Tout ce que vous avez à faire est de convertir l'incorporation de chaque tableau de type «double» en «Vector» avec «Vector.fromList» et de trouver la distance avec «distanceTo».
La distance euclidienne (norme L2) est spécifiée comme méthode de calcul de la «distance».
Enfin, définissez la distance par rapport à la variable membre _distance
et vous avez terminé.
main.dart
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RaisedButton(
onPressed: () async {
var cmpImage = await _readImage();
setState(() {
_cmpImage1 = cmpImage;
});
},
child: Text("Chargement de la première image"),
),
Text("Première image"),
Container(
child:
_cmpImage1 == null ? null : Image.memory(_cmpImage1),
),
],
),
),
Expanded(
flex: 1,
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () async {
var cmpImage = await _readImage();
setState(() {
_cmpImage2 = cmpImage;
});
},
child: Text("Chargement de la deuxième image"),
),
Text("Deuxième image"),
Container(
child:
_cmpImage2 == null ? null : Image.memory(_cmpImage2),
),
],
),
)
],
),
SizedBox(height: 10),
Text("Résultats de comparaison de similarité de visage"),
Text(_getCompareResultString()),
Text("Norme L2$_distance"),
],
),
floatingActionButton: FloatingActionButton(
onPressed: uploadFile,
tooltip: 'Comparaison d'images',
child: Icon(Icons.compare),
),
);
}
C'est un peu long mais simple.
Chargez l'image avec le bouton de chargement de chacune des deux images.
Les données de ʻUint8List de l'image chargée peuvent être affichées à l'écran avec ʻImage.memory
.
Le résultat de la comparaison d'images est affiché en japonais avec _getCompareResultString ()
.
Le processus de calcul de la distance de la sensation d'image est appelé en appelant le service WEB avec ʻonPressed de
FloatingActionButton`.
S'il s'agit d'une image de visage correctement réfléchie, il jugera s'il s'agit de la même personne, donc c'est assez impressionnant. Récemment, il semble y avoir un modèle capable de reconnaître le visage même s'il est masqué. L'utilisation de la reconnaissance faciale pose le problème d'envahir la vie privée, vous devez donc faire attention à son utilisation, mais ce serait amusant si vous pouviez développer un service qui en fasse bon usage.
Recommended Posts