Déployez le modèle de reconnaissance faciale Python sur Heroku et utilisez-le depuis Flutter ②

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.

À propos de l'affiche de cet article

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

Aperçu de l'application

Screenshot_resize.png

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.

Screenshot_1590077756.png

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.

Commentaire de code

Package d'utilisation

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.

Code source Flutter

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.

Chargement des 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 typeFile, 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.

Une fonction qui renvoie le résultat de la comparaison à partir de la distance de la norme L2

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.

Appelant du service WEB

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é.

Partie de dessin d'écran

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`.

finalement

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

Déployez le modèle de reconnaissance faciale Python sur Heroku et utilisez-le depuis Flutter ②
Déployez le modèle de reconnaissance faciale Python sur Heroku et utilisez-le depuis Flutter ①
Déployer et utiliser le modèle de prédiction créé en Python sur SQL Server
Lire et utiliser des fichiers Python à partir de Python
Utiliser le modèle entraîné fastText de Python
Installez mecab sur le serveur partagé Sakura et appelez-le depuis python
Firebase: utilisez Cloud Firestore et Cloud Storage depuis Python
Lier PHP et Python à partir de zéro sur Laravel
Jusqu'à l'utilisation de PhantomJS avec Python sur Heroku
Utilisez python sur Raspberry Pi 3 pour éclairer la LED quand il fait noir!
Installez le sélénium sur votre Mac et essayez-le avec python
J'ai essayé d'envoyer des courriels depuis sendgrid régulièrement avec heroku, avec python
Recevoir des e-mails de Gmail et étiqueter avec Python3
Ubuntu 20.04 sur raspberry pi 4 avec OpenCV et utilisation avec python
Utilisez Thingsspeak de Python
Utiliser fluentd de python
Utilisez MySQL depuis Python
Utiliser MySQL depuis Python
Utilisez BigQuery depuis Python.
Utilisez mecab-ipadic-neologd de Python
Installez Mecab et CaboCha sur ubuntu16.04LTS afin qu'il puisse être utilisé à partir de la série python3
Mettez Ubuntu dans Raspi, mettez Docker dessus et contrôlez GPIO avec python à partir du conteneur
Comment déployer Pybot, le manuel Python le plus simple, sur Heroku
Une note quand j'ai touché l'API de reconnaissance faciale de Microsoft avec Python
Installez pyenv sur MacBookAir et basculez Python à utiliser
J'ai essayé de reconnaître le visage de la vidéo (OpenCV: version python)
[Python] J'ai installé le jeu depuis pip et j'ai essayé de jouer
Utilisez matplotlib sur Ubuntu 12 et Python
Python sur Ruby et Ruby en colère sur Python
Utiliser MySQL depuis Anaconda (python)
Déployer l'application Masonite sur Heroku 2020
Utiliser le modèle django de l'interpréteur
Essayez la reconnaissance faciale avec Python
Utiliser Python sur Windows (PyCharm)
Utilisez l'API e-Stat de Python
traitement pour utiliser les données notMNIST en Python (et essayé de les classer)
Comment installer OpenCV sur Cloud9 et l'exécuter en Python
Obtenez des données de VPS MySQL avec Python 3 et SQL Alchemy
[Python + heroku] De l'état sans Python à l'affichage de quelque chose avec heroku (Partie 1)
Installez lp_solve sur Mac OSX et appelez-le avec python.
[Python + heroku] De l'état sans Python à l'affichage de quelque chose avec heroku (partie 2)
Tout, de la création d'un environnement Python à son exécution sous Windows