[PYTHON] Créez un quiz de dessin avec kivy + PyTorch

Résumé

Cet article est le deuxième jour du calendrier Docomo Dovent.

Connaissez-vous le quiz de dessin? Il s'agit d'un quiz où le questionneur dessine une image et devine ce que les autres dessinent. Il existe de nombreuses applications.

Il semble que même une personne puisse jouer en reconnaissant l'image dessinée par l'utilisateur et en la devinant, donc après L'année dernière, la bibliothèque d'apprentissage automatique [PyTorch](https :: Créons une combinaison de //pytorch.org/) et kivy, une bibliothèque qui vous permet de créer des applications.

abst2.gif

Qu'est-ce que kivy?

Il a le plus d'élan et est facile à utiliser dans la bibliothèque GUI de Python (subjectif).

Il fut un temps où l'on disait que le seuil était élevé parce qu'il n'y avait pas d'informations en japonais, mais maintenant il y a des informations insensées. Grâce aux bénévoles, il existe également un document japonais. Cet article est recommandé comme tutoriel pour les nouveaux utilisateurs.

Environnement de vérification

Formulation de la mise en page

Si vous disposez des fonctions suivantes, il semble que vous puissiez réaliser un quiz de dessin.

--Affichage du thème à dessiner --Bouton pour changer le thème --Partie de toile

Si vous utilisez kviewer, vous pouvez vérifier l'interface utilisateur sans le fichier python du corps principal, donc créons d'abord uniquement la mise en page.

ui_test.kv


BoxLayout:
    orientation: 'vertical' #Disposer verticalement quatre objets enfants
    BoxLayout:
        size_hint_y: 0.1 #Largeur verticale 10%Occuper
        Label: #Afficher le thème
            size_hint_x: 0.7 #Largeur 70%Occuper
            text: 'Draw "XXX"'
        Button: #Bouton pour changer de thème
            size_hint_x: 0.3 #Largeur 30%Occuper
            text: 'Change'
    Label: #Partie toile (provisoire)
        text: 'CANVAS'
        size_hint_y: 0.7 #Largeur verticale 70%Occuper
    BoxLayout:
        size_hint_y: 0.1 #Largeur verticale 10%Occuper
        Button: #Bouton Annuler
            text: 'Undo'
        Button: #Bouton tout effacer
            text: 'Clear'
    Label: #Afficher le résultat de l'IA frappant l'image
        size_hint_y: 0.1 #Largeur verticale 10%Occuper
        text: 'I guess it is "YYY"'

Lorsqu'il est affiché, l'écran suivant apparaît. La disposition est maintenant décidée.

python -m kivy.tools.kviewer ui_test.kv

ui_test.png

Préparation du modèle de reconnaissance

Nous avons besoin d'un modèle pour deviner l'image dessinée par l'utilisateur, mais cette fois nous utiliserons le modèle formé suivant (licence MIT). Au lieu d'être léger, il n'y a que 20 catégories, donc si vous voulez reconnaître les 345 catégories de l'ensemble de données d'origine, veuillez apprendre par vous-même.

Le modèle utilisé cette fois n'utilise pas les informations de série chronologique (ordre dans lequel les lignes sont tracées), mais comme l'ensemble de données d'origine contient également des informations de série chronologique, l'apprentissage dans cet esprit améliorera la précision de la reconnaissance pendant le dessin. Devrait faire. La structure des dossiers est la suivante. Utilisez les deux dossiers "src" et "training_models" du référentiel GitHub ci-dessus.

├─paintquiz.py #Le fichier python à implémenter
├─paintquiz.kv #Fichier Kivy à implémenter
├─src #Dans le référentiel ci-dessus
└─trained_models #Dans le référentiel ci-dessus

la mise en oeuvre

Nous allons implémenter les fonctions nécessaires en python et changer un peu le côté kivy en conséquence. J'ai pu implémenter python avec environ 120 lignes et kivy avec environ 60 lignes, pour un total d'environ 180 lignes. Le canevas est reconnu toutes les secondes et le résultat de la reconnaissance provisoire est affiché. Les fonctions implémentées sont les suivantes.

--Fonctions de chaque bouton définies dans la mise en page

Code Python

paintquiz.py


import random

from kivy.config import Config
#Taille de la fenêtre au démarrage
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '700')

from kivy.app import App
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.graphics import Line, Color

import numpy as np
import cv2
import torch
import torch.nn as nn

#Liste des classes reconnaissables
classes = ["apple", "book", "bowtie", "candle", "cloud", "cup", "door", "envelope", "eyeglasses", "guitar", "hammer",
           "hat", "ice cream", "leaf", "scissors", "star", "t-shirt", "pants", "lightning", "tree"]

class Net():
	#Charger le modèle de reconnaissance
    def __init__(self):
        self.model = torch.load("./trained_models/whole_model_quickdraw", map_location=lambda storage, loc: storage) #Lire le fichier de poids entraîné
        self.model.eval() #Pas d'apprentissage cette fois
        self.sm = nn.Softmax(dim=1) #Si tous les résultats ne sont pas fiables, je veux couper avec le score, donc je normalise avec softmax

	#Entrez le nom du fichier image et renvoyez le résultat de la reconnaissance
    def predict(self, fn, th=.5):
        image = cv2.imread(fn, cv2.IMREAD_UNCHANGED)[:,:,-1] #Obtenez le canal alpha et accédez à l'image binaire
        image = cv2.resize(image, (28, 28))
        image = np.array(image, dtype=np.float32)[None, None, :, :]
        image = torch.from_numpy(image)
        pred = self.model(image)
        pred = self.sm(pred)
        return torch.max(pred[0]), torch.argmax(pred[0]) #Renvoie le score de reconnaissance et la classe de reconnaissance

class Paint(Widget):
    pred_word = StringProperty() #Mot de résultat de reconnaissance
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.line_width = 10 #Épaisseur de ligne
        self.lines = [] #Liste pour stocker les lignes à annuler
        self.in_drawing = False #Juger si le dessin est en cours
        self.canvas.add(Color(0,0,0))
        self.model = Net()
        Clock.schedule_interval(self.predict, 1.0)

    def calc_pos(self, bbox):
        xmin = min(bbox[0], bbox[2])
        ymin = min(bbox[1], bbox[3])
        xmax = max(bbox[0], bbox[2])
        ymax = max(bbox[1], bbox[3])
        return xmin,ymin,xmax,ymax

    #Opération pendant le clic (dessin)
    def on_touch_move(self, touch):
        if self.in_drawing == False:
            if self.pos[0]<touch.x<self.pos[0]+self.size[0] and self.pos[1]<touch.y<self.pos[1]+self.size[1]:
                self.in_drawing = True
                with self.canvas:
                    touch.ud['line'] = Line(points=(touch.x, touch.y), width=self.line_width)
        elif touch.ud:
            if self.pos[0]<touch.x<self.pos[0]+self.size[0] and self.pos[1]<touch.y<self.pos[1]+self.size[1]:
                touch.ud['line'].points += [touch.x, touch.y]

    #Comportement à la fin du clic
    def on_touch_up(self, touch):
        if self.in_drawing:
            self.lines.append(touch.ud['line'])
            self.in_drawing = False

    #Effacer la ligne précédente
    def undo(self):
        if len(self.lines)>0:
            line = self.lines.pop(-1)
            self.canvas.remove(line)

    #Effacer toute la toile
    def clear_canvas(self):
        for line in self.lines:
            self.canvas.remove(line)
        self.lines = []

    #Reconnaître les images toutes les dt secondes
    def predict(self, dt):
        self.export_to_png('image.png')
        with torch.no_grad():
            score, label = self.model.predict('./image.png')
            #Affiché comme inconnu lorsque le score de reconnaissance est inférieur à un certain niveau
            if score < 0.5:
                self.pred_word = "CPU: I have no idea"
            else:
                self.pred_word = 'CPU: I guess it is "{}"'.format(classes[label].upper())

class PaintQuiz(BoxLayout):
    word = StringProperty('Draw "{}"'.format(random.choice(classes).upper())) #Mot de thème
    def __init__(self, **kwargs):
        super(PaintQuiz, self).__init__(**kwargs)
        pass

    def reset(self):
        self.word = 'Draw "{}"'.format(random.choice(classes).upper())

class PaintQuizApp(App):
    def __init__(self, **kwargs):
        super(PaintQuizApp, self).__init__(**kwargs)
        self.title = 'PAINT QUIZ'

    def build(self):
        return PaintQuiz()

if __name__ == '__main__':
    app = PaintQuizApp()
    app.run()

Code Kivy

paintquiz.kv


<PaintQuiz>:
    canvas:
        Color:
            rgb: .9,.9,.9
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        size: root.size
        orientation: 'vertical'
        
        BoxLayout:
            size_hint_y: 0.1
            orientation: 'horizontal'
            Label:
                canvas.before:
                    Color:
                        rgb: 1.,.3,.3
                    Rectangle:
                        pos: self.pos
                        size: self.size
                size_hint_x: 0.7
                text: root.word
                font_size: 18
            Button:
                id: button_reset
                size_hint_x: 0.3
                text: 'Change'
                on_release: root.reset()
        Paint:
            size_hint_y: 0.7
            id: paint_area
            allow_stretch: True
        BoxLayout:
            size_hint_y: 0.1
            Button:
                id: button_undo
                text: 'Undo'
                on_release: paint_area.undo()
            Button:
                id: button_clear
                text: 'Clear'
                on_release: paint_area.clear_canvas()
        Label:
            canvas.before:
                Color:
                    rgb: 1.,.3,.3
                Rectangle:
                    pos: self.pos
                    size: self.size
            size_hint_y: 0.1
            text: paint_area.pred_word
            font_size: 18

Résultat d'exécution

Le thème actuel est affiché en haut à gauche et le résultat de la reconnaissance (ce que vous pensez dessiner) est affiché ci-dessous.

J'ai dessiné des pommes et des ciseaux, mais ils les reconnaissent correctement. Une pomme ressemble à un marteau ou à une feuille lorsque seul l'ourlet est dessiné, mais elle est certainement reconnue comme un marteau ou une feuille. C'est plutôt la bonne réponse car l'ourlet de la pomme = feuilles. apple.gif scissors.gif

en conclusion

Dans la zone d'analyse des données, il y a des gens qui affichent les résultats avec Jupyter Notebook au lieu de Powerpo au moment d'une réunion, mais c'est recommandé car il est facile de faire une application avec ce sentiment et il est facile de s'y intéresser.

En fait, je voulais créer une application de dessin (qui alterne avec l'IA pour dessiner des images), mais uniquement pour les utilisateurs expérimentés de kivy! J'ai abandonné parce que cela semblait être un article. S'il vous plaît, faites quelqu'un et laissez-moi jouer.

Recommended Posts

Créez un quiz de dessin avec kivy + PyTorch
Faire Oui Non Popup avec Kivy
Créez des boutons de type SF avec Kivy
Faites une loterie avec Python
Faire un feu avec kdeplot
Faisons une interface graphique avec python.
Créer un système de recommandation avec python
Créer un filtre avec un modèle django
Faisons un graphe avec python! !!
Faisons un spacon avec xCAT
Créer un itérateur de modèle avec PySide
Faire un joli graphique avec plotly
Faisons un jeu de shiritori avec Python
Créer un lecteur vidéo avec PySimpleGUI + OpenCV
Essayez de dessiner une distribution normale avec matplotlib
[Python] Dessiner un motif de tourbillon avec une tortue
Créez un simulateur de gacha rare avec Flask
Créez un pipeline de notebook avec Kedro + Papermill
Faire une figure partiellement zoomée avec matplotlib
Faisons la voix lentement avec Python
Créez un classificateur en cascade avec Google Colaboratory
Faisons un langage simple avec PLY 1
Faire un circuit logique avec Perceptron (Perceptron multicouche)
Faire une minuterie de lavage-séchage avec Raspberry Pi
Créer une animation GIF avec surveillance des dossiers
Créez un framework Web avec Python! (1)
Faisons une IA à trois yeux avec Pylearn 2
Essayez de dessiner une carte avec python + cartopy 0.18.0
Créez une application de bureau avec Python avec Electron
Faisons un bot Twitter avec Python!
Créez un framework Web avec Python! (2)
Dessinez un graphique avec PyQtGraph Part 1-Drawing
Un mémorandum pour faire WebDAV uniquement avec nginx
Créer un bot Twitter Trend avec heroku + Python
Fabriquer un appareil de surveillance avec un capteur infrarouge
Créez un simple générateur d'images par points avec Flask
Jouez avec PyTorch
Comment créer un dictionnaire avec une structure hiérarchique.
Je veux faire un jeu avec Python
Essayez de créer un code de "décryptage" en Python
Remplaçons UWSC par Python (5) Faisons un robot
Validation croisée avec PyTorch
À partir de PyTorch
Essayez de créer un groupe de dièdre avec Python
Procédure de création d'application multi-plateforme avec kivy
[Chat De Tornado] Créez un chat en utilisant WebSocket dans Tornado
Transformez les données de vacances en une trame de données avec les pandas
Créer un bot LINE WORKS avec Amazon Lex
Essayez de dessiner une carte avec le package folium de Python
J'ai essayé d'utiliser la base de données (sqlite3) avec kivy
(Mémorandum) Créer un diagramme de dispersion 3D avec matplodlib
Combinez des chaînes répétitives en une seule avec des expressions régulières Python.
Faire un bot d'analyse morphologique de manière lâche avec LINE + Flask
Entraînez les données MNIST avec PyTorch en utilisant un réseau neuronal
Faisons un outil de veille de commande avec python
Rendre les indicateurs de performance plus visibles avec PyTorch et scikit-learn
[Pratique] Créez une application Watson avec Python! # 2 [Fonction de traduction]
[Pratique] Créez une application Watson avec Python! # 1 [Discrimination linguistique]