[PYTHON] J'ai essayé d'obtenir les résultats de Hachinai en utilisant le traitement d'image

Postscript (02/09/2017)

J'ai changé de programme parce que quatre balles ont été ajoutées aux notes.

Qu'est-ce que Hachinai

Il s'agit d'un jeu social de baseball basé sur l'expérience pour les jeunes, sorti en juin 2017. Le titre officiel est "August Cinderella Nine"

En un mot, c'est un jeu dans lequel vous élevez des personnages, passez des commandes et regardez le match. J'ai l'impression de faire un opener power pro. Il semble que le traitement du jeu soit bien fait, et de cette manière (bien qu'il y ait quelques points à craindre), vous pouvez vérifier les résultats de frappe pour chaque jeu.

Screenshot_20170901-014026.png

Cependant, il n'est pas possible de vérifier les résultats totaux du joueur dans ce jeu. Pour passer un ordre approprié, vous devez gérer vos propres notes, conditions et adversaires. Comme il est difficile de saisir manuellement les notes, j'ai créé un programme pour obtenir automatiquement les notes en utilisant le traitement d'image.

Cible

environnement

Langue utilisée

Python3.5

Bibliothèque utilisée

Méthode spécifique

Détection de nombre

Cette fois, nous détecterons les nombres à l'aide de la correspondance de modèles sans utiliser de techniques difficiles telles que l'apprentissage automatique. Comme le montre la figure ci-dessous, la correspondance de modèle est une méthode de recherche de l'image cible tout en décalant progressivement la partie qui correspond au modèle. テンプレートマッチング.PNG

Cette fois, la correspondance des modèles est effectuée à l'aide des fonctions fournies par OpenCV. De plus, les images suivantes sont préparées pour la mise en correspondance des modèles. Puisqu'elle est convertie en image en échelle de gris lors du traitement, je pense que la différence de couleur n'a pas beaucoup d'effet. テンプレ群.PNG

Reconnaissance du ton

La reconnaissance des tons est plus simple, prenez la différence entre les valeurs de pixel de l'image cible et du modèle et sélectionnez celle avec la plus petite différence. 差分計算.PNG

Code source

# -*- coding: utf-8 -*-

import sys
import cv2
import numpy as np
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
import PyQt5.QtWidgets as QtWidgets
import pandas as pd

#Convertir les images OpenCV afin qu'elles puissent être affichées avec PyQt
#Utilisez ce code source
#http://qiita.com/odaman68000/items/c8c4093c784bff43d319
def create_QPixmap(image):
    qimage = QtGui.QImage(image.data, image.shape[1], image.shape[0], image.shape[1] * image.shape[2], QtGui.QImage.Format_RGB888)
    pixmap = QtGui.QPixmap.fromImage(qimage)
    return pixmap

#Effectuer la mise en correspondance des modèles
def matching(img,num,threshold,img_res,cell_y,cell_x):
    template = cv2.imread('./template/number/{}.png'.format(num),0)
    template = template[6:-6,:]
    w, h = template.shape[::-1]

    res = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED)
    loc = np.where( res >= threshold)
    res_loc = []
    for pt in zip(*loc[::-1]):
        #Exclure les doublons détectés
        flag=True
        for pt2 in res_loc:
            if pt2[0] + w > pt[0]:
                flag = False
        if flag:
            res_loc.append(pt)
            #Dessinez les nombres et les cadres détectés sur l'image d'origine
            cv2.rectangle(img_res, (pt[0]+cell_x, pt[1]+cell_y), (pt[0]+cell_x+w, pt[1]+cell_y+h), (0,0,255), 2)
            n = "-" if num == "mai" else num
            cv2.putText(img_res, str(n), (pt[0]+cell_x,pt[1]+cell_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
    return res_loc

#La fenêtre qui s'ouvre lorsque vous déposez une image
class Add_widget(QtWidgets.QDialog):

    def __init__(self,frame,clipboard,parent=None):
        super(Add_widget, self).__init__(parent)
        self.initUI(frame,clipboard,parent)

    def initUI(self,frame,clipboard,parent):
        self.lbl = QtWidgets.QLabel()
        self.frame = frame

        self.datatable = QtWidgets.QTableWidget()
        self.datatable.setColumnCount(9)
        self.datatable.setRowCount(9)

        self.spinlbl = QtWidgets.QLabel("threshold")
        self.spinbox = QtWidgets.QDoubleSpinBox()
        self.spinbox.setRange(0,1)
        self.spinbox.setSingleStep(0.01)
        self.spinbox.setValue(0.90)
        self.spinbox.valueChanged.connect(self.get_result)
        self.sbin_hbox = QtWidgets.QHBoxLayout()
        self.sbin_hbox.addWidget(self.spinlbl)
        self.sbin_hbox.addWidget(self.spinbox)
        self.sbin_hbox.addStretch(1)

        self.button = QtWidgets.QPushButton("copy to clipboard")
        self.button.clicked.connect(self.copy_to_clipboard)

        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.addWidget(self.lbl)
        self.vbox.addWidget(self.datatable)
        self.vbox.addLayout(self.sbin_hbox)
        self.vbox.addWidget(self.button)
        self.setLayout(self.vbox)
        self.setWindowTitle('result')
        self.clipboard = clipboard

        self.get_result()

    #Mise à jour avec les notes obtenues à partir du tableau
    def update_table(self,df):
        for i in range(len(df.index)):
            for j in range(len(df.columns)):
                self.datatable.setItem(i,j,QtWidgets.QTableWidgetItem(str(df.get_value(i, j))))

    #Identifier la tonalité et détecter les nombres
    def detection_value(self,frame,threshold):
        img_res = frame.copy()
        img_gray = cv2.cvtColor(img_res, cv2.COLOR_BGR2GRAY)

        df = pd.DataFrame()
        li=[0,2,3,2,2,3,2,3,2]

        #Obtenez les notes ligne par ligne
        for row in range(9):
            player_list = []

            #Identification du ton
            condi_cell = frame[210+sum(li[:row+1])+(84*(row)):210+sum(li[:row+1])+(84*(row+1)),687:758]
            condi_list = np.zeros(5)

            for i in range(5):
                condi = cv2.imread("./template/condition/{}.png ".format(i))
                #Calculez la valeur de la différence
                sad = np.sum(np.abs(np.mean(condi_cell.astype(np.float32),axis=(0,1))-np.mean(condi.astype(np.float32),axis=(0,1))))
                #sad = np.sum(np.abs(condi_cell.astype(np.float32) - condi.astype(np.float32)))
                condi_list[i] = sad
            #Sélectionnez l'image avec la plus petite différence
            c = np.argmin(condi_list)
            player_list.append(c+1)
            cv2.putText(img_res, str(c+1), (687, 210+sum(li[:row+1])+(84*(row+1))), cv2.FONT_HERSHEY_PLAIN, 4, (0, 0, 0), 5)

            #Diviser par colonne
            for col in range(8):
                cell_y = 210+sum(li[:row+1])+(84*(row))
                cell_width = 105 if col < 7 else 128
                cell_x = 759+col*105
                img_cell = img_gray[cell_y:cell_y+84,cell_x:cell_x+cell_width]
                list_num = []

                #0~Effectuer une correspondance de modèle jusqu'à 9
                for num in range(10):
                    loc = matching(img_cell,num,threshold,img_res,cell_y,cell_x)
                    for pt in loc:
                        list_num.append([num,pt[0],pt[1]])

                #Trier par coordonnée x
                list_num.sort(key=lambda x:(x[1]))   

                #Concaténer les nombres triés par coordonnée x
                s = ""
                for i in range(len(list_num)):
                    #Dans le cas du taux au bâton"0."Attacher
                    if col == 6 and i == 0:
                        s += "0."
                    s += "{}".format(list_num[i][0])
                    #Pour RC, après le premier numéro"."(En supposant que RC est rarement à deux chiffres)
                    if col == 7 and i == 0:
                        s += "."
                #Le taux de frappeur combiné est enfin"0.100"Si ça devient"1.00"(En supposant qu'il n'y a pas 1 coup en 10 coups dans une partie)
                if col == 6 and s == "0.100":
                    s = "1.00"
                #Si le numéro ne peut pas être détecté-Réglé sur 10000
                try:
                    res_num = float(s)
                except ValueError:
                    res_num = -10000.0
                #Lorsque RC est détecté, la correspondance de modèle est effectuée pour moins, et s'il y a moins, il est multiplié par -1.
                if col == 7:
                    loc = matching(img_cell,"mai",threshold,img_res,cell_y,cell_x)
                    if len(loc) > 0:
                        res_num *= -1
                player_list.append(res_num)
            #Ajoutez des notes ligne par ligne en utilisant des pandas
            se = pd.Series(player_list)
            df = df.append(se, ignore_index=True)

        return img_res, df

    #Copiez le contenu du tableau dans le presse-papiers
    def copy_to_clipboard(self):
        s = ""
        for r in range(self.datatable.rowCount()):
            for c in range(self.datatable.columnCount()):
                try:
                    s += str(self.datatable.item(r,c).text()) + "\t"
                except AttributeError:
                    s += "\t"
            s = s[:-1] + "\n"
        self.clipboard.setText(s)

    #Obtenez des notes
    def get_result(self):
        img_res, df = self.detection_value(self.frame,self.spinbox.value())
        self.update_table(df)

        img_res = cv2.cvtColor(img_res, cv2.COLOR_BGR2RGB)
        img_res = cv2.resize(img_res, (1280,720))
        qt_img = create_QPixmap(img_res)
        self.lbl.setPixmap(qt_img)

    def show(self):
        self.exec_()

#Classe QLabel prenant en charge le glisser-déposer
class DropLabel(QtWidgets.QLabel):
    def __init__(self,parent):
        super().__init__(parent)
        self.parent = parent
        self.setAcceptDrops(True)
        self.setAlignment(QtCore.Qt.AlignCenter);
        self.setText("Drop here")

    def dragEnterEvent(self, e):
            e.accept()

    def dropEvent(self, e):
        mimeData = e.mimeData()
        files = [u.toLocalFile() for u in mimeData.urls()]
        for f in files:
            print("loading {}".format(f))
            #Chargez l'image déposée
            frame = cv2.imread(f)
            #Si la lecture échoue, aucun traitement n'est effectué
            if frame is not None:
                frame = cv2.resize(frame, self.parent.size)
                add_widget = Add_widget(frame,self.parent.clipboard)
                add_widget.show()

#Fenêtre pour déposer une image
class Image_widget(QtWidgets.QWidget):

    def __init__(self,clipboard):
        super().__init__()

        self.initUI(clipboard)

    def initUI(self,clipboard):
        self.height = 1080
        self.width = 1920
        self.size = (self.width,self.height)
        self.clipboard = clipboard

        self.lbl = DropLabel(self)
        self.lbl.resize(640,480)

        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.addWidget(self.lbl)
        self.setWindowTitle('hachinai')
        self.show()
        sys.exit(app.exec_())

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    clipboard = app.clipboard()
    screen = Image_widget(clipboard)

Exécution du programme

Pour exécuter ce programme, il est nécessaire de mettre l'image comme le montre la figure de la méthode concrète dans "template / number /" et "template / condition /". Lorsque vous exécutez le programme, vous verrez la fenêtre sombre suivante. 起動.png

Si vous faites glisser et déposez l'image dans cette fenêtre, vous obtiendrez la note. Les images contenant du japonais dans le chemin ne peuvent pas être lues. 取得.png

Puisque la cellule de note est acquise à la position absolue, elle ne peut pas être reconnue correctement si la table se déplace de manière significative. Si la reconnaissance échoue dans le cas contraire, la modification du seuil peut fonctionner. Ensuite, vous pouvez copier la note en cliquant sur "Copier dans le presse-papiers" et la coller dans Excel. エクセル.PNG

en conclusion

Pour le moment, j'ai pu atteindre mon objectif. Quant à l'impression que j'ai utilisée, je pense qu'il sera un peu plus facile de gérer les notes si elle est combinée avec la macro Excel.

référence

Correspondance de modèles avec OpenCV Afficher les données d'image gérées par OpenCV / numpy sur Qt Widget [GUI avec Python] PyQt5 -Widget II- Introduction à la création d'applications d'interface graphique multiplateforme avec PyQt of Python Fastest way to populate QTableView from Pandas data frame copy pyqt table selection, including column and row headers [Faites glisser et déposez des fichiers avec PyQt](http://doloop while.hatenablog.com/entry/20100108/1275174371)

Recommended Posts

J'ai essayé d'obtenir les résultats de Hachinai en utilisant le traitement d'image
J'ai essayé d'obtenir l'index de la liste en utilisant la fonction énumérer
J'ai essayé de corriger la forme trapézoïdale de l'image
J'ai essayé d'utiliser le filtre d'image d'OpenCV
J'ai essayé de transformer l'image du visage en utilisant sparse_image_warp de TensorFlow Addons
J'ai essayé de compresser l'image en utilisant l'apprentissage automatique
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
J'ai essayé d'extraire le texte du fichier image en utilisant Tesseract du moteur OCR
J'ai essayé d'obtenir une base de données sur les courses de chevaux en utilisant Pandas
J'ai essayé de créer l'image de démarrage SD de LicheePi Nano
J'ai essayé d'obtenir une liste de noms AMI en utilisant Boto3
J'ai essayé d'obtenir une image en grattant
J'ai essayé d'estimer la similitude de l'intention de la question en utilisant Doc2Vec de gensim
J'ai essayé de résoudre 100 traitements linguistiques Knock version 2020 [Chapitre 3: Expressions régulières 25-29]
J'ai essayé d'obtenir le code d'authentification de l'API Qiita avec Python.
J'ai essayé d'extraire et d'illustrer l'étape de l'histoire à l'aide de COTOHA
J'ai essayé d'obtenir automatiquement le RSS de la chanson la plus populaire de l'iTunes Store
J'ai essayé d'obtenir les informations sur le film de l'API TMDb avec Python
J'ai essayé l'histoire courante de l'utilisation du Deep Learning pour prédire la moyenne Nikkei
En utilisant COTOHA, j'ai essayé de suivre le cours émotionnel de la course aux meros.
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé de prédire la détérioration de la batterie lithium-ion en utilisant le SDK Qore
J'ai essayé de résoudre la version 2020 de 100 problèmes de traitement du langage [Chapitre 3: Expressions régulières 20 à 24]
J'ai essayé de résoudre la version 2020 de 100 coups de traitement de langue [Chapitre 1: Mouvement préparatoire 00-04]
J'ai essayé d'automatiser le travail de masquage du visage de l'image de coordination pour l'usure
J'ai essayé de résoudre la version 2020 de 100 traitements linguistiques [Chapitre 1: Mouvement préparatoire 05-09]
J'ai essayé de détecter l'iris à partir de l'image de la caméra
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé d'obtenir une AMI en utilisant AWS Lambda
J'ai essayé d'approcher la fonction sin en utilisant le chainer
J'ai essayé d'utiliser l'API de Sakenowa Data Project
J'ai essayé de visualiser les informations spacha de VTuber
J'ai essayé d'effacer la partie négative de Meros
[Python] J'ai essayé d'obtenir Json de squid ring 2
J'ai essayé d'identifier la langue en utilisant CNN + Melspectogram
J'ai essayé de compléter le graphe de connaissances en utilisant OpenKE
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé de résumer les opérations de chaîne de Python
J'ai essayé d'obtenir les informations du site .aspx qui est paginé à l'aide de Selenium IDE aussi sans programmation que possible.
J'ai essayé de prédire la victoire ou la défaite de la Premier League en utilisant le SDK Qore
J'ai essayé de notifier la mise à jour de "Devenir romancier" en utilisant "IFTTT" et "Devenir un romancier API"
Python pratique 100 coups J'ai essayé de visualiser l'arbre de décision du chapitre 5 en utilisant graphviz
Je voulais collecter beaucoup d'images, j'ai donc essayé d'utiliser "google image download"
J'ai essayé de trier les objets de l'image du plat de steak-④ Clustering
J'ai essayé de comparer la vitesse de traitement avec dplyr de R et pandas de Python
J'ai essayé la "correction gamma" de l'image avec Python + OpenCV
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de refactoriser le modèle CNN de TensorFlow en utilisant TF-Slim
J'ai essayé de simuler l'optimisation des publicités à l'aide de l'algorithme Bandit
J'ai essayé d'obtenir les informations du Web en utilisant "Requests" et "lxml"
J'ai essayé la reconnaissance faciale du problème du rire en utilisant Keras.
Je veux obtenir des informations sur le fonctionnement de Yahoo Route
Traitement linguistique 100 knocks-29: Obtenez l'URL de l'image du drapeau
[Python] J'ai essayé de visualiser la relation de suivi de Twitter
[TF] J'ai essayé de visualiser le résultat de l'apprentissage en utilisant Tensorboard
[Apprentissage automatique] J'ai essayé de résumer la théorie d'Adaboost
[Python] J'ai essayé de collecter des données en utilisant l'API de wikipedia
J'ai essayé de combattre le minimum local de la fonction Goldstein-Price