[PYTHON] Gestion des notes de Hachinai à l'aide de SQL

Cette fois, j'ai rendu possible l'ajout des résultats obtenus dans "J'ai essayé d'obtenir les résultats des hits de Hachinai en utilisant le traitement d'image" à la base de données, et dans la base de données Je veux gérer mes notes.

Postscript (08/09/2017)

Les données collectées lors de la Princess Cup sont publiées sur le blog. Journal des résultats Hachinai

Cible

environnement

Langue utilisée

Python3.5

Bibliothèque utilisée

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
import json
import sqlite3

#Convertir les images OpenCV afin qu'elles puissent être affichées dans 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+6)
        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.button = QtWidgets.QPushButton("Ajouter à la base de données")
        self.button.clicked.connect(self.add_database)

        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):

        try:
            f = open("player.json", 'r')
            player_data = json.load(f)
        except UnicodeDecodeError:
            f = open("player.json", 'r', encoding='utf-8')
            player_data = json.load(f)

        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 = []

            player_list.append(player_data["date"])
            player_list.append(player_data["opponent"])

            player_list.append(player_data["player{}".format(row+1)]["scene"])
            player_list.append(player_data["player{}".format(row+1)]["name"])
            player_list.append(player_data["player{}".format(row+1)]["position"])
            #Ordre de grève
            player_list.append(row+1)
            player_list.append(player_data["team_buff"])

            #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(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 sûr 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, elle est multipliée 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)

        self.df = df
        return img_res
    
    #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)

    #Ajouter à la base de données
    def add_database(self):
        try:
            db_name = "hachinai.db"
            con = sqlite3.connect(db_name)
            for i in range(9):
                con.execute("insert into results("\
                            + "date,"\
                            + "opponent,"\
                            + "scene,"\
                            + "name,"\
                            + "position,"\
                            + "batting_order,"\
                            + "team_buff,"\
                            + "condition,"\
                            + "at_bat,"\
                            + "hit,"\
                            + "homerun,"\
                            + "RBI,"\
                            + "BB,"
                            + "base_hit,"\
                            + "batting_average,"\
                            + "RC"\
                            + ")"\
                            + " values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",tuple(self.df.ix[i]))
            con.commit()
            con.close()
            self.accept()
        except sqlite3.OperationalError:
            w = Message_Widget()
            w.exec_()

    #Obtenez des notes
    def get_result(self):
        img_res = self.detection_value(self.frame,self.spinbox.value())
        self.update_table(self.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_()

#Affichage des messages d'erreur
class Message_Widget(QtWidgets.QMessageBox):
    def __init__(self,parent=None):
        super(Message_Widget, self).__init__(parent)
        self.initUI(parent)

    def initUI(self,parent):
        self.setText("La base de données n'existe pas")
        self.setIcon(QtWidgets.QMessageBox.Warning)
        self.setStandardButtons(QtWidgets.QMessageBox.Close)

#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,self)
                add_widget.show()

#Fenêtre pour déposer l'image
class Hachinai_widget(QtWidgets.QWidget):

    def __init__(self,clipboard=None,parent=None):
        super(Hachinai_widget, self).__init__(parent)
        super().__init__()

        self.initUI(clipboard,parent)

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

        self.lbl = DropLabel(self)
        self.lbl.setMinimumSize(640,480)
        self.lbl.setFrameStyle(QtWidgets.QFrame.Box | QtWidgets.QFrame.Plain)

        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.addWidget(self.lbl)
        self.setLayout(self.vbox)
        self.setWindowTitle('hachinai')
        self.show()
        

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

Préparation de l'exécution du programme

Créer une table

Avant d'exécuter le programme ci-dessus, exécutez le programme suivant pour créer un tableau de résultats qui stocke vos notes. Cette table est enregistrée dans un fichier appelé hachinai.db.


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

import sqlite3

db_name = "hachinai.db"
con = sqlite3.connect(db_name)

#Confirmation de l'existence de la table
cur = con.execute("SELECT * FROM sqlite_master WHERE type='table' and name='results'")
if cur.fetchone() == None: #Faites-le parce qu'il n'existe pas
    con.execute("CREATE TABLE results("\
                + "id INTEGER PRIMARY KEY,"\
                + "date DATE,"\
                + "opponent TEXT,"\
                + "scene TEXT,"\
                + "name TEXT,"\
                + "position TEXT,"\
                + "batting_order INTEGER,"\
                + "team_buff TEXT,"\
                + "condition INTEGER,"\
                + "at_bat INTEGER,"\
                + "hit INTEGER,"\
                + "homerun INTEGER,"\
                + "RBI INTEGER,"\
                + "BB INTEGER,"\
                + "base_hit INTEGER,"\
                + "batting_average DOUBLE,"\
                + "RC DOUBLE)"\
                )
    con.commit()
con.close() 

Description des informations sur le joueur

Cette fois, les informations du joueur sont décrites dans un fichier appelé player.json et ajoutées à la note en la lisant sur le programme.

player.json


{
    "date":"2017-11-06",
    "opponent":"Match de Cendrillon",

    "team_buff":"0",

    "player1":{
        "scene":"Point de départ de rêve",
        "name":"Tsubasa Arihara",
        "position":"6"
    },

    "player2":{
        "scene":"Sous le même ciel",
        "name":"Blé Akino",
        "position":"3"
    },

    "player3":{
        "scene":"Tiret précipité!",
        "name":"Aya Taketomi",
        "position":"8"
    },

    "player4":{
        "scene":"C'est l'endroit pour jouer!",
        "name":"Chie Hebei",
        "position":"4"
    },

    "player5":{
        "scene":"Je veux me démarquer davantage!",
        "name":"Osaka ici",
        "position":"9"
    },

    "player6":{
        "scene":"Coup d'essai passionnant",
        "name":"Yoshimi Iwaki",
        "position":"5"
    },

    "player7":{
        "scene":"Étirement soigneux",
        "name":"Hiiragi Kotoha",
        "position":"7"
    },

    "player8":{
        "scene":"Stratégie approfondie",
        "name":"Waka Suzuki",
        "position":"2"
    },

    "player9":{
        "scene":"Une âme de balle!",
        "name":"Yuhime Nozaki",
        "position":"1"
    }
}

"_2" est un personnage usé. La formation est plus compliquée que la principale.

Exécution du programme

Lorsque vous exécutez le programme, vous verrez une fenêtre comme celle-ci. 起動.png

Vous pouvez obtenir des notes en supprimant l'image. À ce stade, les informations sur le joueur sont lues à partir de player.json. qiitaSQL実行.PNG

Si vous cliquez ici sur «Ajouter à la base de données», les données seront ajoutées à la table créée précédemment et la fenêtre de notation sera fermée.

Gestion des notes

Exécutez PupSQLite et ouvrez hachinai.db. Ensuite, double-cliquez sur les résultats dans les tableaux pour afficher les notes enregistrées comme indiqué ci-dessous. データ一覧.PNG

Cliquez sur "Entrée d'instruction SQL" -> "Ouvrir la fenêtre de requête" pour afficher l'écran de saisie SQL. Ici, vous pouvez afficher les résultats totaux du lecteur en entrant et en exécutant l'instruction SQL suivante.

SELECT scène,Nom,Position défensive,État, round((Les coups*1.0) / (Nombre de coups*1.0), 3)comme taux au bâton,Nombre de parties,Au bâton, Nombre de coups, Les coups,Base d'accueil,Point de point,Quatre balles,Coup de base, round(((Les coups +Quatre balles)*1.0) / (Au bâton*1.0), 3)comme taux de base, round((Coup de base*1.0) / (Nombre de coups*1.0), 3)aussi long taux de réussite,  round(((Les coups +Quatre balles)*1.0) / (Au bâton*1.0) + (Coup de base*1.0) / (Nombre de coups*1.0), 3) as OPS, RC  from ( 
SELECT scène comme scène,nom comme nom,position comme position défensive, round(avg(condition), 2)comme condition, count(scene)en nombre de jeux, sum(at_bat)comme au bat, sum(at_bat) - sum(BB)en nombre de coups,  sum(hit)comme hits, sum(homerun)comme base d'origine, sum(RBI)comme point, sum(BB)comme quatre balles, sum(base_hit)comme coup de base, round(avg(RC), 2) as RC FROM results GROUP BY scene ) ;results GROUP BY scene ) ;

SQL実行.PNG

Vous pouvez également trier en cliquant sur le nom de la colonne. opsソート.PNG

En regardant les données, j'ai le sentiment que les joueurs en bonne forme ont tendance à bien performer. Je pense que la raison pour laquelle le taux de réussite de Kana Kuju est si élevé est qu'elle a dépassé la limite deux fois. Je ne sais pas pourquoi Minako Nitta a un taux de succès long.

De plus, diverses données peuvent être sorties en concevant l'instruction SQL. Par exemple, si vous modifiez l'instruction SQL comme suit, vous pouvez obtenir la note totale pour chaque condition.

SELECT scène,Nom,Position défensive,État, round((Les coups*1.0) / (Nombre de coups*1.0), 3)comme taux au bâton,Nombre de parties,Au bâton, Nombre de coups, Les coups,Base d'accueil,Point de point,Quatre balles,Coup de base, round(((Les coups +Quatre balles)*1.0) / (Au bâton*1.0), 3)comme taux de base, round((Coup de base*1.0) / (Nombre de coups*1.0), 3)aussi long taux de réussite,  round(((Les coups +Quatre balles)*1.0) / (Au bâton*1.0) + (Coup de base*1.0) / (Nombre de coups*1.0), 3) as OPS, RC  from ( 
SELECT scène comme scène,nom comme nom,position comme position défensive, round(avg(condition), 2)comme condition, count(scene)en nombre de jeux, sum(at_bat)comme au bat, sum(at_bat) - sum(BB)en nombre de coups,  sum(hit)comme hits, sum(homerun)comme base d'origine, sum(RBI)comme point, sum(BB)comme quatre balles, sum(base_hit)comme coup de base, round(avg(RC), 2) as RC FROM results WHERE scene = 'Prendre une pause' GROUP BY condition ) ;

SQL調子別.PNG

en conclusion

La gestion des données est devenue beaucoup plus simple car je n'ai plus à copier et coller les résultats dans Excel. Je souhaite collecter des données lors d'un match de classement qui aura lieu prochainement.

Recommended Posts

Gestion des notes de Hachinai à l'aide de SQL
Méthode de connexion SQL utilisant pyodbc
Analyser les données au format CSV à l'aide de SQL