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.
Les données collectées lors de la Princess Cup sont publiées sur le blog. Journal des résultats Hachinai
Python3.5
# -*- 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_())
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()
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.
Lorsque vous exécutez le programme, vous verrez une fenêtre comme celle-ci.
Vous pouvez obtenir des notes en supprimant l'image. À ce stade, les informations sur le joueur sont lues à partir de player.json.
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.
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.
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 ) ;
Vous pouvez également trier en cliquant sur le nom de la colonne.
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 ) ;
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.