tkinter + socket + keras recognize minst handwritten digits


The network programming course is designed to do what you want, with code + experimental report attached.

Environment configuration

python 3.7.3
pymysql 1.0.2
Tensorflow GPU 2.0.0 (cpu can also be used)
numpy 1.19.2
tk 8.6.10

MySQL 8.0.23
Navicat version
It's not critical. The environment setting is mainly related to tf

code

Click to download the github code

Server

Neural network model required cnn.h5 It can be obtained from other training and can be at the bottom reference From github, I rewrite the prototype.

import socket
import pickle
import numpy
import pymysql
from tensorflow import keras

# Default tcp mode transmission
sk = socket.socket()
sk2 = socket.socket()
# Binding IP and port
ip_port = ('192.168.43.31', 8888)
ip_port2 = ('192.168.43.31', 8889)
# Binding listening
sk.bind(ip_port)
sk2.bind(ip_port2)
# maximum connection
sk.listen(5)
sk2.listen(5)
#Loading the trained cnn model
model=keras.models.load_model("cnn.h5")
#Connect to local database
con = pymysql.connect(host='localhost',
                       user='root',
                       password='0208',
                       db='client_list',
                       charset='utf8')
cursor = con.cursor()
log_in = 0

def send_from(arr, dest):
  view = memoryview(arr).cast('B')
  while len(view):
    nsent = dest.send(view)
    view = view[nsent:]
 
def recv_into(arr, source):
  view = memoryview(arr).cast('B')
  while len(view):
    nrecv = source.recv_into(view)
    view = view[nrecv:]

# Receive data continuously
while True:
    # Prompt information
    print("Waiting to receive data.......")
    # Receive data connection object and customer address
    con2, address = sk2.accept()
    while True:
        user = con2.recv(1024).decode()
        sql = "select password from password where username = '" + user + "'"
        cursor.execute(sql)
        #print("searching")
        password=con2.recv(1024).decode()
        for i in cursor.fetchall():#Return a tuple
            #print(i)
            if password in i:
                log_in = 1
        con2.send(pickle.dumps(log_in))
        if log_in == 1:
            break
    
    
    #Login process
    conn, address = sk.accept()
    # Constantly receive messages from customers
    while True:
        # Receive client messages
        if log_in == 1:
            print('receiving data')
            data=numpy.zeros(784).reshape(1,28,28,1).astype(float)
            recv_into(data, conn)
            #print(data)
        # Exit command received
            if data.sum() == 0:
                print('EXIT')
                log_in = 0
                break

            rec = model.predict_classes(data)[0]
            conn.send(pickle.dumps(rec))
        
    # Actively close the connection
    conn.close()

client

Main function main py

# -*- coding:utf-8 -*-
from tkinter import Tk
from Window import Window
import socket

if __name__ == '__main__':
    win=Tk()
    ww = 530#Window width setting 530
    wh = 430#Window height setting 430
    # The server adopts TCP mode, and the client also adopts TCP mode. The default parameter is TCP
    client = socket.socket()
        # IP and port to access the server
    ip_port= ('192.168.43.31', 8888)
        # Connect host
    client.connect(ip_port)
    Window(win,ww,wh,client)
    win.protocol("WM_DELETE_WINDOW",Window.closeEvent)
    win.mainloop()

Class window py

# -*- coding:utf-8 -*-
from tkinter import *
from PIL import Image,ImageDraw
import numpy as np
import pickle
import socket
import sys

def send_from(arr, dest):
  view = memoryview(arr).cast('B')
  while len(view):
    nsent = dest.send(view)
    view = view[nsent:]
 
def recv_into(arr, source):
  view = memoryview(arr).cast('B')
  while len(view):
    nrecv = source.recv_into(view)
    view = view[nrecv:]


class Window:
    def __init__(self,win,ww,wh,client):
        self.win=win
        self.ww=ww
        self.wh=wh
        self.client=client
        self.win.geometry("%dx%d+%d+%d" %(ww,wh,500,100))
        self.win.title("Handwritten numeral recognition software---by 20180620 Cryptobranchoidea ")
        self.can=Canvas(self.win,width=ww-130,height=wh-32,bg='white')
        self.can.place(x=0,y=0)
        self.can.bind("<B1-Motion>",self.paint)
        
        self.entry1=Entry(self.win,width=16)
        self.entry1.place(x=405,y=200)
        self.entry2=Entry(self.win,width=16)
        self.entry2.place(x=405,y=260)
        
        self.label1=Label(self.win,text="Identification results:",font=('Microsoft YaHei ',20))
        self.label1.place(x=405,y=0)
        self.label2=Label(self.win,width=6,height=2,text='',font=('Microsoft YaHei ',20),
                          background="white",relief="ridge",borderwidth=10)
        self.label2.place(x=405,y=50)
        self.label3=Label(self.win, text="user name:",font=('Microsoft YaHei ',10))
        self.label3.place(x=405,y=170)
        self.label4=Label(self.win, text="password:",font=('Microsoft YaHei ',10))
        self.label4.place(x=405,y=230)

        self.button1=Button(self.win,text="Predict",width=10,height=1,bg='gray',command=self.predict)
        self.button1.place(x=10,y=wh-30)
        self.button2=Button(self.win,text="Clear",width=10,height=1,bg='white',command=self.clear)
        self.button2.place(x=100,y=wh-30)
        self.button3=Button(self.win,text="Exit", width=10,height=1,bg='white',command=self.exit_)
        self.button3.place(x=200,y=wh-30)
        self.button4=Button(self.win,text="Log in", width=10,height=1,bg='white',command=self.log_in)
        self.button4.place(x=280,y=wh-30)

        self.image=Image.new("RGB",(ww-130,wh-30),color=(0,0,0))#(0,0,0) indicates a black background
        self.draw=ImageDraw.Draw(self.image)
        self.valid_flag = 0
        
        self.client2 = socket.socket()
        # IP and port to access the server
        self.ip_port2= ('192.168.43.31', 8889)
        # Connect host
        self.client2.connect(self.ip_port2)
        
    def log_in(self):
        user=self.entry1.get().encode()
        password=self.entry2.get().encode()
        self.client2.send(user)
        self.client2.send(password)
        _data=self.client2.recv(1024)
        self.valid_flag=pickle.loads(_data)       
        if self.valid_flag == 1:    
            messagebox.showinfo(title='Login successful', message='welcome!')
        else: 
            messagebox.showwarning(title='Login failed', message='Wrong user name or password!')
            
    def paint(self,event):
        self.x1,self.y1=event.x-12,event.y-12
        self.x2,self.y2=(self.x1+24),(self.y1+24)
        self.can.create_rectangle(self.x1, self.y1, self.x2, self.y2, fill="black")
        self.draw.rectangle(((self.x1,self.y1),(self.x2,self.y2)),(255,255,255))#(255255255) indicates white words

    def predict(self):
        if self.valid_flag== 0 :
            messagebox.showwarning(title='warning', message='Please log in first')
        else:
            if np.array(self.image).sum()==0:#It is detected that the prediction has not been handwritten, and the prediction result is empty
                self.display('')
            else:
                self._image=self.image.resize((28,28),Image.ANTIALIAS).convert('L')
                self._image=np.array(self._image).reshape(1,28,28,1).astype(float)#shape is (- 1,28,28,1) during training
                #self.rec=self.model.predict_classes(self._image.astype(float))[0]
                send_from(self._image, self.client)
                _data = self.client.recv(1024)
                data = pickle.loads(_data)
                #self.client.send(pickle.dumps(self._image))
                self.display(data)#Display forecast results
                self.draw=ImageDraw.Draw(self.image)

    def clear(self):
        self.can.delete("all")
        self.image=Image.new("RGB",(self.ww-130,self.wh-30),(0,0,0))#(0,0,0) indicates a black background
        self.draw=ImageDraw.Draw(self.image)
        self.display('')

    def display(self,string):
        self.label2=Label(self.win,width=6,height=2,text=string,font=('Microsoft YaHei ',20),
                          background="white",relief="ridge",borderwidth=10)
        self.label2.place(x=405,y=50)

    def exit_(self):
        if self.valid_flag ==1:
            _image=np.zeros(784).reshape(1,28,28,1).astype(float)
            send_from(_image, self.client)
        self.client2.close()
        self.win.destroy()

    def closeEvent():
        Window.win.destroy()
        sys.exit()

Experimental report

1, Overall function description

1. Function overview

Neural networks in artificial intelligence are being more and more widely used in various fields of society. Using artificial neural network to process data in stand-alone mode not only needs to embed the huge trained neural network model into the program, but also needs to load the neural network into memory every time the program is started, which is time-consuming. Therefore, taking mnist data set as an example, we originally wrote a set of handwritten numeral neural network recognition program based on client server mode of LAN.

2. General functional drawing

Client Yellow & server green

3. Flow chart of each sub function

3.1 server initialization

Include must include: socket, numpy, pickle, pymysql, and keras in tensorflow.
tcp protocol binds and listens to two ports: tcp mode, which binds the server-side ip address and two ports (one is used to log in and query the database, and the other is used to send recognized handwritten digits) to bind and listen.
Load the neural network training model: load the trained through the keras package h5 neural network model.
Link to local database: connect to the local database through pymysql and obtain the database cursor.
Set login status to No: if no, the client cannot send detection information through another port. Pop up at the same time.

3.2 client initialization


Include packages: socket, numpy, pickle, tkinter, PIL.
tcp protocol binding listens to two ports: tcp mode, which connects the two ports of the server ip address (one is used to log in and query the database, and the other is used to send recognized handwritten digits). If successful, no error will be reported.
Create visual interface: create a visual interface through tkinter, including function buttons, prompt information, text boxes, etc. Set the width and height of the visual program interface and bind various functions.
Set login status to No: if no, the client cannot send detection information through another port. Pop up at the same time.

3.3 user login + database query


See II. Description of database and III. description of communication for details

3.4 interactive drawing (client paint function, clear function)

When the mouse falls, moves and refreshes: during initialization, bind the canvas canvas to be valid when left clicking. When left clicking, call the paint function to draw a black box on the canvas continuously according to the landing point of the captured mouse event.
When the Clear button is pressed, the canvas is cleared and the label2 prediction result is also cleared.

3.5 recognition and detection of handwritten digits through neural network (client predict function)

See description of communication section III for detailed data flow diagram

3.6 exit and sever links (client exit function)


See description of communication section III for detailed data flow diagram

2, Database function description

1. Database overview

The database is used to store user name and password information. MySQL database is selected as the database, which is used on the server, and the client does not call the database. Database creation (user information entry) and visualization using Navicat software, calling database in python, using pymysql to expand functions in packages.

2. Database E-R diagram

3. Data dictionary

Field namedata typeDefault valueNon null allowedAuto incrementremarks
usernamestringNO0user name
passwordstringNO0password

4. Data flow diagram

3, Description of communication part

1. Communication security

In order to ensure communication security, we have made the following considerations during program development:

The database is arranged as the local database of the server, rather than the client accessing the server database through port 3306. The server accesses the database locally and organizes the database network connection, which can ensure that the server user name and password are not leaked.
The login success flag is set on both the client and the server. After receiving the information sent by the client through port 1 (8888 in the program), the server returns the correct flag after finding the correct password in the database, and sets the login flag of the client on the server to correct; If the password is found to be wrong, the error flag will be returned. The client receives a flag followed by a login success flag set according to the flag. When and only when the server flag is correct, it starts to receive data from port 2 (8889 in the program). When the client login flag is incorrect, the prediction will not send numpy array to the server through port 2. At this time, clicking the exit button function will not send an empty array to the server to disconnect the link, but directly disconnect the link of port 1 (because the link of port 2 has not been established at this time). At the same time, the login success flag is set to prevent the client from sending the data to be identified to the server without login.

2. Communication mode

Networking: the client and server are connected to the mobile hotspot LAN. In the experiment, the IP address of the server is 192.168.43.31. Two ports are used, in which one port (8888) is used for login message confirmation, and the other port (8889) is used for sending and returning identification information of numpy array to be identified. The database adopts local database and is not connected to the network. (however, we have realized the connection of database in LAN. We do not use network database, just to ensure communication security.
Communication protocol: it adopts the communication protocol encapsulated by socket package in python. Socket provides TCP and UDP protocol sending methods. This program uses TCP protocol to send data. Sending data needs to be sent in byte stream (byte class). This program uses pickle package to package int class and send short messages; For the numpy array of 28*28 float type exceeding the length limit of TCP message sent, the method of reading and sending by memory is adopted. For strings (user name / password), use the python string class's own encode() and The decode() method is converted to a byte stream.

4, Testing

1. Function test

The procedure is generally complete, and the defects are as follows:
(1) You must press the exit key in the program to exit, but you cannot press the X in the upper right corner to exit. Because the link establishment is encapsulated inside the class, the link cannot be called when the form is destroyed by pressing x, so pressing x will cause the unexpected interruption of the link.

(2) When you are not logged in, pressing exit to exit will cause the link for login to be accidentally disconnected, and the link cannot be established next time you log in.
Possible improvement methods: move the link establishment steps outside the class, or break the link in the subclass by inheritance, and use the form structure as the parent class.

2. Efficiency test

Due to the limited number of notebooks, the program has only tested the connection of one laptop. It is expected that there will still be bug s when connecting multiple clients.
We preprocess the handwritten digits to be recognized at the client, compress the 160X160 array into a 28 * 28 array that needs to be input into the neural network, and then send it, which greatly improves the efficiency of data transmission.

Others want to say

The school's network programming course requires the group to think of a topic and do it by themselves. It was written in ddl on the last day. In addition to the UI, as long as you can send data through the network (socket). Remember to turn off the firewall. mysql database should be built by yourself. Just change the server statement. There are many bug s, too lazy to fix~

Keywords: MySQL socket TensorFlow

Added by nielsene on Sun, 23 Jan 2022 03:37:48 +0200