手写数字识别(Android版)服务端 介绍 服务端实现的需求:
接收到手机发来的图片并存储到本地
识别本地存储的图片
将识别结果传回手机
使用flask服务器和http协议
实现 训练和预测部分和之前的类似,可以参考之前写的文章
网络 识别网络可以自定义成识别效果更好的,文件名为:network.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import torchimport torch.nn as nnclass net (nn.Module ): def __init__ (self ): super(net, self).__init__() self.Conn_layers = nn.Sequential( nn.Linear(784 , 100 ), nn.Sigmoid(), nn.Linear(100 , 10 ), nn.Sigmoid() ) def forward (self, input ): output = self.Conn_layers(input) return output
训练 和之前写的类似,超参数、损失函数和优化器可根据自己实际情况调整,文件名为:train.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 import torchimport torch.nn as nnimport torch.optim as optimfrom torchvision import datasets, transformsfrom torch.autograd import Variablefrom torch.utils.data import DataLoaderfrom network import *train_dataset = datasets.MNIST(root='./data/' , train=True , transform=transforms.ToTensor(), download=False ) test_dataset = datasets.MNIST(root='./data/' , train=False , transform=transforms.ToTensor(), download=False ) batch_size = 100 train_loader = torch.utils.data.DataLoader(dataset = train_dataset, batch_size = batch_size, shuffle=True ) test_loader = torch.utils.data.DataLoader(dataset = test_dataset, batch_size = batch_size, shuffle = True ) LR = 0.1 net = net() loss_function = nn.CrossEntropyLoss() optimizer = optim.SGD( net.parameters(), lr = LR, momentum = 0.9 , weight_decay = 0.0005 ) epoch = 20 for epoch in range(epoch): for i, data in enumerate(train_loader): inputs, labels = data inputs = inputs.reshape(batch_size, 784 ) inputs, labels = Variable(inputs), Variable(labels) outputs = net(inputs) loss = loss_function(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() test_result = 0 for data_test in test_loader: images, labels = data_test images = images.reshape(batch_size, 784 ) images, labels = Variable(images), Variable(labels) output_test = net(images) for i in range(len(labels)): if torch.argmax(output_test[i]) == labels[i]: test_result += 1 print("Epoch {} : {} / {}" .format(epoch, test_result, len(test_dataset))) torch.save(net.state_dict(), 'weight/test.pth' )
图像预处理 文件名为:pretreatment.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 import cv2import numpy as npdef image_preprocessing (): img = cv2.imread("getImage/image.jpg" ) gray_img = cv2.cvtColor(img , cv2.COLOR_BGR2GRAY) gauss_img = cv2.GaussianBlur(gray_img, (5 ,5 ), 0 , 0 , cv2.BORDER_DEFAULT) img_edge1 = cv2.Canny(gauss_img, 100 , 200 ) high = img.shape[0 ] width = img.shape[1 ] add_width = np.zeros(high, dtype = int) add_high = np.zeros(width, dtype = int) for h in range(high): for w in range(width): add_width[h] = add_width[h] + img_edge1[h][w] for w in range(width): for h in range(high): add_high[w] = add_high[w] + img_edge1[h][w] acount_high_up = np.argmax(add_width) acount_high_down = np.argmax(add_width) while add_width[acount_high_up] != 0 : acount_high_up = acount_high_up + 1 while add_width[acount_high_down] != 0 : acount_high_down = acount_high_down - 1 acount_width_left = np.argmax(add_high) acount_width_right = np.argmax(add_high) while add_high[acount_width_left] != 0 : acount_width_left = acount_width_left - 1 while add_high[acount_width_right] != 0 : acount_width_right = acount_width_right + 1 width_spacing = acount_width_right - acount_width_left high_spacing = acount_high_up - acount_high_down poor = width_spacing - high_spacing if poor > 0 : tailor_image = img[acount_high_down - poor \ // 2 - 5 :acount_high_up + poor - poor \ // 2 + 5 , acount_width_left - 5 :acount_width_right + 5 ] else : tailor_image = img[acount_high_down - 5 :acount_high_up + 5 , \ acount_width_left + poor // \ 2 - 5 :acount_width_right - poor + poor // 2 + 5 ] gray_img = cv2.cvtColor(tailor_image , cv2.COLOR_BGR2GRAY) gauss_img = cv2.GaussianBlur(gray_img, (5 ,5 ), 0 , 0 , cv2.BORDER_DEFAULT) zoom_image = cv2.resize(gauss_img, (28 , 28 )) high = zoom_image.shape[0 ] wide = zoom_image.shape[1 ] for h in range(high): for w in range(wide): if zoom_image[h][w] > 100 : zoom_image[h][w] = 0 else : zoom_image[h][w] = 255 - zoom_image[h][w] return zoom_image
预测 这里和之前写的不同的是把预测部分封装成一个函数,这样方便传递结果,文件名为:predict.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import torchfrom network import *import pretreatment as PREnet = net() net.load_state_dict(torch.load('weight/test.pth' )) def predict_number (): img = PRE.image_preprocessing() inputs = img.reshape(-1 , 784 ) inputs = torch.from_numpy(inputs) inputs = inputs.float() predict = net(inputs) return torch.argmax(predict).detach().numpy()
服务器 文件名为:my_server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from flask import Flaskfrom flask import requestimport osfrom werkzeug.utils import secure_filenamefrom predict import *app = Flask(__name__) @app.route('/') def test (): return '服务器正常运行' @app.route('/upload', methods=['POST']) def upload (): f = request.files['file' ] print('连接成功' ) basepath = os.path.dirname(__file__) upload_path = os.path.join(basepath, 'getImage' , secure_filename(f.filename)) f.save(upload_path) my_result = predict_number() print(my_result) return str(my_result) if __name__ == '__main__' : app.run(host='0.0.0.0' , port=5555 )
效果