当前位置:   article > 正文

Python :MNIST手写数据集识别 + 手写板程序 最详细,直接放心,大胆地抄!跑不通找我,我包教!

Python :MNIST手写数据集识别 + 手写板程序 最详细,直接放心,大胆地抄!跑不通找我,我包教!

目录

一、背景与总概

二、研究对象

三、数据表示与结构

四、选择的模型

五、效果展示 

六、画板部分

七、直接抄,直接方向跑!


所有的工程文件百度云连接,包含模型!

链接:https://pan.baidu.com/s/1RlS0aVPOFwmNBPBHmcF0SQ 
提取码:MATR 
--来自百度网盘超级会员V3的分享

注意:人工智能预测的闪退问题是在main文件同目录下没有image_rgzn的文件夹,添加就好。

一、背景与总概

        利用Python语言编写和调试一个识别手写数字图像的三层深度前馈网络,包括数据预处理,网络模型构建,模型参数初始化和正向推理,反向梯度下降参数寻优,最后模型预测的功能。目的是学会基本的深度网络模型建立、训练和推理过程,理解深度网络的实现原理。

        通过自己学习人工智能之后,发现了三个的重要经验和总结,第一个是你对你研究事物本质的理解;第二个是,将你研究事物进行数据化,找到一个合理的数据表示以及数据的结构;第三个是,寻找与这个数据表示的形式和结构合适的“模型”(即模型算法等)。依据上面数据到模型的这个思路,对下文进行一个描写

        首先,介绍一下MNIST手写数据集,这就是我们研究的事物了。该数据集包含60,000个用于训练的示例和10,000个用于测试的示例。数据集包含了0-9共10类手写数字图片,每张图片都做了尺寸归一化,都是28x28大小的灰度图。

训练集图像:train-images-idx3-ubyte.gz(9.9MB,包含60000个样本)
训练集标签:train-labels-idx1-ubyte.gz(29KB,包含60000个标签)
测试集图像:t10k-images-idx3-ubyte.gz(1.6MB,包含10000个样本)
测试集标签:t10k-labels-idx1-ubyte.gz(5KB,包含10000个标签)

下图展示一些具体例子:

        其次是手写板程序,在PyQt5实例 画板小程序_pyqt5画图板_CreatorGG的博客-CSDN博客的程序基础之上添加了,1人工智能预测,2预测结果展示的文本框。

        传统机器学习的问题与缺陷随着深度学习的发展被得到解决,深度学习也可以说是神经网络的重命名,他是建立在多层非线性的神经网络结构之上,对数据表示进行抽象的一系列机器学习。深度学习的出现使得图像,语言得到突破性的发展。本此处理的数据为图像,所以最后我本次选用的模型是人工智能深度神经网络(一般的神经网络)。你也可以使用卷积神经网络模型,卷积神经网络是对将局部的特征十分的敏感,正确率会更高。

二、研究对象

        在你的PycharmIDE里创建一个function.py的文件,在里面定义如下函数:

         1:jiexi_image(path),此函数需要一个字符串对象的输入,是两个训练集和测试集的图像文件地址,返回的对象是一个numpy.array的对象。

  1. def jiexi_image(path):
  2. # 用二进制读取
  3. data = open(path, 'rb').read()
  4. offset = 0
  5. fmt_header = '>iiii'
  6. magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, data, offset)
  7. print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))
  8. image_size = num_rows * num_cols
  9. offset += struct.calcsize(fmt_header)
  10. fmt_image = '>' + str(image_size) + 'B'
  11. images = np.empty((num_images, num_rows, num_cols))
  12. for i in range(num_images):
  13. if (i + 1) % 10000 == 0:
  14. print('已解析 %d' % (i + 1) + '张')
  15. images[i] = np.array(struct.unpack_from(fmt_image, data, offset)).reshape((num_rows, num_cols))
  16. offset += struct.calcsize(fmt_image)
  17. return images
'
运行

        2: jiexi_label(path) ,传入参数是训练集和测试集的两个label标签文件地址,是一个字符串对象,返回的也是一个numpy.array的对象。

  1. def jiexi_label(path):
  2. data = open(path, 'rb').read()
  3. offset = 0
  4. fmt_header = '>ii'
  5. magic_number, num_images = struct.unpack_from(fmt_header, data, offset)
  6. print('魔数:%d, 图片数量: %d张' % (magic_number, num_images))
  7. # 解析数据集
  8. offset += struct.calcsize(fmt_header)
  9. fmt_image = '>B'
  10. labels = np.empty(num_images)
  11. for i in range(num_images):
  12. if (i + 1) % 10000 == 0:
  13. print('已解析 %d' % (i + 1) + '张')
  14. labels[i] = struct.unpack_from(fmt_image, data, offset)[0]
  15. offset += struct.calcsize(fmt_image)
  16. return labels
'
运行

        3:plot_data(images,labels,n,issave = False),传入图像,image是一个numpy.array对象;传入的标签,labels是一个numpy.array对象;传入的issave是一个判断逻辑值,如果是真就保存图片,但是一般是不保存的。

  1. def plot_data(images,labels,n,issave = False):
  2. for i in range(n):
  3. print(labels[i])
  4. plt.imshow(images[i], cmap='gray')
  5. plt.show()
  6. # if(issave == True):
  7. # plt.savefig(fname = "save"+str(datetime.datetime.now())+".jpg")
  8. print('done')
'
运行

         接下来在你的工程文件夹下建立一个train.py 文件,在里面利用function.py里你设定的函数来解析训练集图像和测试集图像,训练集标签和测试集标签,然后利用plot_data函数打印数据,查看是否对应。

  1. import function
  2. #start1 = time.time()
  3. train_image_path = './MNIST/train-images-idx3-ubyte/train-images.idx3-ubyte'
  4. train_lable_path = './MNIST/train-labels-idx1-ubyte/train-labels.idx1-ubyte'
  5. teat_image_path = './MNIST/t10k-images-idx3-ubyte/t10k-images.idx3-ubyte'
  6. teat_lable_path = './MNIST/t10k-labels-idx1-ubyte/t10k-labels.idx1-ubyte'
  7. # #加载数据
  8. train_image = function.jiexi_image(train_image_path)
  9. train_lable = function.jiexi_label(train_lable_path)
  10. teat_image = function.jiexi_image(teat_image_path)
  11. test_lable = function.jiexi_label(teat_lable_path)
  12. # print(train_image.shape)
  13. function.plot_data(train_image, train_lable, 10, True)

        最终效果:

                      

        可以看出是正确的。 

三、数据表示与结构

        既然train_image, train_lable, teat_image , test_lable 是numpy.array对象,那么我们就可以对他进行操作,对他进行打印输出,print(train_image):

         什么也看不出来,那我们打印一下他的维度来看看, ​​​,是一个三维数组。

        打印train_image[0]来看:

         然后可以推知,我们要处理的对象结构是如下图所示。

四、选择的模型

        传统机器学习的问题与缺陷随着深度学习的发展被得到解决,深度学习也可以说是神经网络的重命名,他是建立在多层非线性的神经网络结构之上,对数据表示进行抽象的一系列机器学习。深度学习的出现使得图像,语言得到突破性的发展。本此处理的数据为图像,所以最后我本次选用的模型是人工智能深度神经网络(一般的神经网络)。你也可以使用卷积神经网络模型,卷积神经网络是对将局部的特征十分的敏感,正确率会更高。

        在你的工程文件下创建一个DeepNET.py的文件,里面是深度神经网络所需要的各种函数。从零开始,从理论到代码实现无论是在研究和学习都是十分有帮助的,希望我和各位读者都保持住这个习惯。

        本次,假设你已经有一定的知识储备了,如梯度下降法的本质,神经网络结构基本清楚,如果不清楚就十分推荐,deeplearning的吴大师的视频教程 [双语字幕]吴恩达深度学习deeplearning.ai_哔哩哔哩_bilibili 教的非常细致。

        第一步,导入库,在DeepNET.py的文件里完善搭建深度神经网络所需要的函数。

        深度神经网络概述,Deep Neural Networks, 深度神经网络,以下简称DNN。DNN里最基本的单元是神经元模型。每个神经元与其他神经元相连,当他“兴奋”时,就会向相连的神经元发送物质,改变神经元的电位。如果某个神经元的电位超过了一个阀值,那么它就会被激活。结果抽象可以得到沿用至今的M_P神经元模型。

        线性部分,是简单的相乘相加,激活部分是利用激活函数处理得到输出。常见的激活函数有sigmoid,relu等,本次采用的激活函数是relu函数。

        由神经元组成的多层神经网络,如图所示。有输入层,输出层以及中间隐含层。每一个输入线性求合,通过激活函数,传到下一个神经元,我们大可不必一个个的去算,我们可以使用向量化来使得我们的程序更加简洁。

        梯度下降法:

 

  

(下面为了简单我以输入的对象是28*28规格图片,第一层隐含单元有200个,第二层隐含单元是100个,输出层为10个的网络结构阐述。)

一般的构造网络的流程:

        初始化超参数(包括启动深度神经网络的权值w,偏执b)--》向前传播(线性部分+激活函数)--》 计算代价 --》 反向传播 (激活函数反向,线性部分反向)--》更新超参数.

基础部分和总概 网络结构为 [28*28 200 100 10]

        学习人工智能,应该理论应用于实践,应该多动手进行数学演算,将演算用代码实现,最后进行总结于改进。

(1)对这个网络的结构要有一个清楚的认识

        在草稿本上进行矩阵维数的测试 

 

下面是对于想要了解代码一个个看清楚流程的去看,如果想直接跑通,代码在最后!

1、导入库,是导入一些必要的库

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import datetime
  4. import os
  5. import sys
  6. import h5py

2、定义所需要的softmax函数

  1. def sigmoid(Z):
  2. A = 1/(1+np.exp(-Z))
  3. cache = Z
  4. return A, cache
  5. def sigmoid_backward(dA, cache):
  6. Z = cache
  7. s = 1/(1+np.exp(-Z))
  8. dZ = dA * s * (1-s)
  9. return dZ
  10. # relu函数 和反向求导
  11. def relu(Z):
  12. A = np.maximum(0,Z)
  13. cache = Z
  14. return A, cache
  15. def relu_backward(dA, cache):
  16. Z = cache
  17. dZ = np.array(dA, copy=True)
  18. dZ[Z <= 0] = 0
  19. return dZ
  20. # Softmax
  21. def softmax(Z):
  22. A = np.exp(Z)/np.sum(np.exp(Z),axis=0)
  23. cache = Z
  24. return A, cache
'
运行

        在此我把relu和sigmoid函数也给出,方便读者后续的使用,可以利用这个去做逻辑回归啊什么什么的,增加泛用性。

3、初始化参数

        使用梯度下降法的首要步骤就是初始化参数,这个点是随机的。        

        输入的是 layers_dims是一个向量,是你的网络结构。返回的是一个字典对象parameters,里面有超参数w和b。、

        恭喜你,走出了第一步,完成了初始化参数的步骤。

  1. def init_W(layers_dims):
  2. np.random.seed(3)
  3. parameters = {}
  4. L = len(layers_dims)
  5. for l in range(1, L):
  6. parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(layers_dims[l - 1])
  7. parameters["b" + str(l)] = np.zeros((layers_dims[l], 1))
  8. return parameters
'
运行

4.1、向前传播之线性部分

        y = \sum w*x+b,也就是输入x与权值相乘与偏执相加。是一层的线性部分。

  1. def L_forword_sum(W,A,b):
  2. Z = np.dot(W,A)+b
  3. cache = (A,W,b)
  4. return Z,cache
'
运行

 4.2、向前传播之激活函数向前

        隐含层使用的是relu函数,输出层使用的是softmax函数,参考(311条消息) 入门级都能看懂的softmax详解_bitcarmanlee的博客-CSDN博客

(311条消息) ReLU函数简介_潇湘_AQ的博客-CSDN博客

        本函数是基于线性部分建立的一个输入是A_prev是上一层的输出,W,b是本层的网络参数,activation是激活函数的名字,用于选择用哪一个函数。返回值是一个元组,包含A隐含层输出的激活值,和用于反向传播时的重要数据。

  1. def L_activate_forworld(A_prev,W,b,activation):
  2. if activation == "relu":
  3. Z ,linear_cache = L_forword_sum(W,A_prev,b)
  4. A, activation_cache = relu(Z)
  5. elif activation == "sigmoid":
  6. Z, linear_cache = L_forword_sum(W, A_prev, b)
  7. A, activation_cache = sigmoid(Z)
  8. elif activation == "softmax":
  9. Z, linear_cache = L_forword_sum(W, A_prev, b)
  10. A, activation_cache = softmax(Z)
  11. cache = (linear_cache, activation_cache)
  12. return A,cache
'
运行

4.3、向前传播函数接口

        线性部分+激活函数组成一次向前传播,线性部分的输出输入到激活函数最后得到的最终值。

        函数输入x是初始的输入值即图片的大小,parameters是超参数字典。经过一个for循环可以完成整个向前传播。

        恭喜你,你的网络利用这个函数就可以进行向前传播了。

  1. def L_forword(X, parameters):
  2. caches = []
  3. A = X
  4. L = len(parameters) // 2
  5. for l in range(1, L):
  6. A_prev = A
  7. A, cache = L_activate_forworld(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], "relu")
  8. caches.append(cache)
  9. # 最后一层使用softmax
  10. AL, cache = L_activate_forworld(A, parameters['W' + str(L)], parameters['b' + str(L)], "softmax")
  11. caches.append(cache)
  12. return AL, caches
'
运行

 5、计算代价之交叉熵代价函数

        (311条消息) 交叉熵损失函数(Cross Entropy Loss)_SongGu1996的博客-CSDN博客

        你可以计算你的网络的代价了,每个网络都希望做到收敛快,代价小。

  1. def cost(Y_out,Y):
  2. cost = -np.sum(np.multiply(np.log(Y_out), Y)) / Y_out.shape[1]
  3. cost = np.squeeze(cost)
  4. return cost
'
运行

5.1、反向传播之线性部分的反向

        感兴趣的可以到我的草稿上看。

  1. def linear_backward(dZ, cache):
  2. A_prev, W, b = cache
  3. m = A_prev.shape[1]
  4. dW = np.dot(dZ, A_prev.T) / m
  5. db = np.sum(dZ, axis=1, keepdims=True) / m
  6. dA_prev = np.dot(W.T, dZ)
  7. return dA_prev, dW, db
'
运行

 5.2、反向传播之激活函数的反向

        感兴趣的可以到我的草稿上看。

  1. def linear_activation_backward(dA, cache, Y,activation="relu"):
  2. linear_cache, activation_cache = cache
  3. if activation == "relu":
  4. dZ = relu_backward(dA, activation_cache)
  5. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  6. elif activation == "sigmoid":
  7. dZ = sigmoid_backward(dA, activation_cache)
  8. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  9. elif activation == "softmax":
  10. dZ = dA - Y
  11. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  12. return dA_prev, dW, db
'
运行

5.3、 反向传播函数接口

        将激活函数的反向和线性部分的反向组织好后,封装为一个反向函数接口。

        恭喜你,你已经做好了反向传播了。

  1. def L_model_backward(AL, Y, caches,case):
  2. grads = {}
  3. L = len(caches)
  4. m = AL.shape[1]
  5. Y = Y.reshape(AL.shape)
  6. dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
  7. if case == "softmax":
  8. current_cache = caches[L - 1]
  9. grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y,"softmax")
  10. elif case == "sigmoid":
  11. current_cache = caches[L - 1]
  12. grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y, "sigmoid")
  13. for l in reversed(range(L - 1)):
  14. current_cache = caches[l]
  15. dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, Y ,"relu")
  16. grads["dA" + str(l + 1)] = dA_prev_temp
  17. grads["dW" + str(l + 1)] = dW_temp
  18. grads["db" + str(l + 1)] = db_temp
  19. return grads
'
运行

 6、利用梯度下降法 更新参数

         梯度下降法是一个十分好的优化算法,他的目的更改参数使代价优化到最小。

  1. def update_parameters(parameters, grads, learning_rate):
  2. L = len(parameters) // 2 # 整除
  3. for l in range(L):
  4. parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
  5. parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
  6. return parameters
'
运行

7、神经网络搭建

       依据下面的过程,将你的函数,像拼拼图一样做出来。

        初始化超参数(包括启动深度神经网络的权值w,偏执b)--》向前传播(线性部分+激活函数)--》 计算代价 --》 反向传播 (激活函数反向,线性部分反向)--》更新超参数

        for循环是来进行重复训练的。.

        X, 输入的样本集;Y,label集;        

        net_layers,网络结构;learning_rate=0.0075, 学习率;num_iterations=3000,迭代次数;step =1, 步长;print_cost=False, 是否打印代价;isPlot=True,是否绘制代价—迭代次数图;

  1. def deepnet(X, Y,net_layers,learning_rate=0.0075, num_iterations=3000,step =1, print_cost=False, isPlot=True):
  2. np.random.seed(1) #设计种子
  3. costs = [] #用于画图
  4. parameters = init_W(net_layers)
  5. for i in range(0, num_iterations):
  6. # 迭代
  7. AL, caches = L_forword(X, parameters)
  8. costi = cost(AL, Y) #这里的Y是标准化的Y
  9. grads = L_model_backward(AL, Y, caches,"softmax")
  10. parameters = update_parameters(parameters, grads, learning_rate)
  11. if i % step == 0:
  12. # 记录成本
  13. costs.append(costi)
  14. # 是否打印成本值
  15. if print_cost:
  16. print("第", i, "次迭代,成本值为:", np.squeeze(costi))
  17. if isPlot:
  18. plt.plot(np.squeeze(costs))
  19. plt.ylabel('cost')
  20. plt.xlabel('iterations (per tens)')
  21. plt.title("Learning rate =" + str(learning_rate))
  22. plt.show()
  23. # plt.savefig(fnme = "cast"+str(datetime.datetime.now())+".jig")
  24. return parameters
'
运行

 8、测试集合测试

        将得到的模型,用于测试集看准确率。

  1. def predict(X, y, parameters,Y_org):
  2. m = X.shape[1]
  3. n = len(parameters) // 2 # 神经网络的层数
  4. p = np.zeros((1, m))
  5. # 根据参数前向传播
  6. probas, caches = L_forword(X, parameters)
  7. p = np.argmax(probas,axis=0)
  8. zql = float(np.sum((p == Y_org)) / m)
  9. print("准确度为: " + str(float(np.sum((p == Y_org)) / m)))
  10. error_list = []
  11. for i in range(m):
  12. if p[i] != Y_org[i]:
  13. error_list.append(i)
  14. return p,error_list,zql
'
运行

 9、保存模型

        你所训练好的网络不仅仅只是训练好就完了,你要应用它,就得保存下来,我这里有两个保存函数,一个是保存为txt文本,一个是保存为h5文件,建议使用h5文件,也可以像vvg19那样使用.mat文件数据,只要是数据,你就可以打开,打开的接口可以自己写,如果自己写的话一般是学习其结构,转化为二进制在转化为你想要的数据,不过,基本有大佬写出来了,不要闭门造车。

  1. def save_model(parameters):
  2. np.set_printoptions(threshold=sys.maxsize)
  3. model_number = 0
  4. f = open("model/model" + str(model_number) + ".txt", "a+")
  5. f.write(str(datetime.datetime.now()) + "\n")
  6. f.write("model_number " + str(model_number) + "\n")
  7. for i, j in parameters.items():
  8. f.write(str(i) + "\n")
  9. f.write(str(j) + "\n")
  10. f.close()
  11. return 0
  12. #保存为h5数据格式
  13. def save_h5(data,layers,zql):
  14. str1 = "./model/model"+str(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))+".h5"
  15. f = h5py.File(str1, "w")
  16. ID = ["model layer "]
  17. f.create_dataset("layers",data = layers)
  18. i = len(data) // 2
  19. for j in range(i):
  20. f.create_dataset("W"+str(j+1),data = data["W"+str(j+1)])
  21. f.create_dataset("b"+str(j+1),data = data["b"+str(j+1)])
  22. f.create_dataset("accuracy",data = zql)
  23. f.close()
'
运行

10、利用模型预测和读取h5数据格式的模型

        保存好了数据,就是如何读取我们的网络参数,进行运用人工智能深度神经网络。利用这两个函数就可以。

  1. def predict1(X, parameters):
  2. # 根据参数前向传播
  3. probas, caches = L_forword(X, parameters)
  4. p = np.argmax(probas,axis=0)
  5. return p
  6. def read_ccs(path):
  7. w = h5py.File(path, "r")
  8. layers = w["layers"][:]
  9. l = len(layers)
  10. p = {}
  11. # print(l)
  12. for i in range(1, l):
  13. p["W" + str(i)] = w["W" + str(i)][:]
  14. p["b" + str(i)] = w["b" + str(i)][:]
  15. return p, layers
'
运行

 第二步,有了相应功能的函数,我们就可以进行训练。train.py里的具体内容如下。

  1. import DeepNET
  2. import time
  3. import function
  4. if __name__ == '__main__':
  5. # 计时开始
  6. start1 = time.time()
  7. train_image_path = './MNIST/train-images-idx3-ubyte/train-images.idx3-ubyte'
  8. train_lable_path = './MNIST/train-labels-idx1-ubyte/train-labels.idx1-ubyte'
  9. teat_image_path = './MNIST/t10k-images-idx3-ubyte/t10k-images.idx3-ubyte'
  10. teat_lable_path = './MNIST/t10k-labels-idx1-ubyte/t10k-labels.idx1-ubyte'
  11. # #加载数据
  12. train_image = function.jiexi_image(train_image_path)
  13. train_lable = function.jiexi_label(train_lable_path)
  14. teat_image = function.jiexi_image(teat_image_path)
  15. test_lable = function.jiexi_label(teat_lable_path)
  16. # print(train_image.shape)
  17. function.plot_data(train_image,train_lable,10,True)
  18. train_image = train_image.reshape(train_image.shape[0], -1).T / 255
  19. teat_image = teat_image.reshape(teat_image.shape[0], -1).T / 255
  20. train_lable1 = function.label_init(train_lable)
  21. test_lable1 = function.label_init(test_lable)
  22. print(train_image.shape)
  23. end1 = time.time()
  24. start2 = time.time()
  25. layers = [784, 200, 150, 10]
  26. parameters = DeepNET.deepnet(train_image, train_lable1,layers , learning_rate=0.0075, num_iterations=3000,
  27. step=100, print_cost=True, isPlot=True)
  28. end2 = time.time()
  29. p ,error_list_train,zql1 = DeepNET.predict(train_image, train_lable1, parameters, train_lable)
  30. p0 ,error_list_test ,zql2 = DeepNET.predict(teat_image,test_lable1,parameters,test_lable)
  31. zql = [[zql1],[zql2]]
  32. print("数据加载时间:",end1-start1," 秒")
  33. print("模型训练时间:",end2-start2," 秒")
  34. DeepNET.save_h5(parameters,layers,zql)

五、效果展示 

(1)训练情况

(2)模型保存情况 h5数据情况

 最终效果

 

         综合来说,没有加优化正确率也是不错的,感兴趣的可以对网络进行优化。

六、画板部分

      这里参考的是(311条消息) PyQt5实例 画板小程序_pyqt5画图板_CreatorGG的博客-CSDN博客

对qtpy5感兴趣的可以去学习一下。

        整个手绘板加训练的程序在最后给出。

七、直接抄,直接方向跑!

训练网络,运行train.py。

训练出自己的网络后,使用main.py运行利用使用你的模型。

文件结构和目录:

         创建我红线的文件。

(1)function.py代码:

  1. #此文件是一些函数 有加载数据模块
  2. import datetime
  3. import struct
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from PIL import Image
  7. import os
  8. def jiexi_image(path):
  9. # 用二进制读取
  10. data = open(path, 'rb').read()
  11. offset = 0
  12. fmt_header = '>iiii'
  13. magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, data, offset)
  14. print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))
  15. image_size = num_rows * num_cols
  16. offset += struct.calcsize(fmt_header)
  17. fmt_image = '>' + str(image_size) + 'B'
  18. images = np.empty((num_images, num_rows, num_cols))
  19. for i in range(num_images):
  20. if (i + 1) % 10000 == 0:
  21. print('已解析 %d' % (i + 1) + '张')
  22. images[i] = np.array(struct.unpack_from(fmt_image, data, offset)).reshape((num_rows, num_cols))
  23. offset += struct.calcsize(fmt_image)
  24. return images
  25. def jiexi_label(path):
  26. data = open(path, 'rb').read()
  27. offset = 0
  28. fmt_header = '>ii'
  29. magic_number, num_images = struct.unpack_from(fmt_header, data, offset)
  30. print('魔数:%d, 图片数量: %d张' % (magic_number, num_images))
  31. # 解析数据集
  32. offset += struct.calcsize(fmt_header)
  33. fmt_image = '>B'
  34. labels = np.empty(num_images)
  35. for i in range(num_images):
  36. if (i + 1) % 10000 == 0:
  37. print('已解析 %d' % (i + 1) + '张')
  38. labels[i] = struct.unpack_from(fmt_image, data, offset)[0]
  39. offset += struct.calcsize(fmt_image)
  40. return labels
  41. def plot_data(images,labels,n,issave = False):
  42. for i in range(n):
  43. print(labels[i])
  44. plt.imshow(images[i], cmap='gray')
  45. plt.show()
  46. # if(issave == True):
  47. # plt.savefig(fname = "save"+str(datetime.datetime.now())+".jpg")
  48. print('done')
  49. ## 说明:输入原始图像路径和新建图像文件夹名称 默认修改出长度宽度为64*64
  50. def stdimage(pathorg, name, pathnew=None, width=64, length=64):
  51. # 检查文件是否建立
  52. if pathnew == None: # 如果没有手动创建
  53. tage = os.path.exists(os.getcwd() + '\\' + name) # 检查一下是否属实
  54. if not tage: # 没有整个新文件夹
  55. os.mkdir(os.getcwd() + "\\" + name) # 创建文件夹,name
  56. image_path = os.getcwd() + "\\" + name + "\\"
  57. else: # 已经手动创建
  58. tage = os.path.exists(pathnew + "\\" + name)
  59. if not tage:
  60. path = os.getcwd()
  61. os.mkdir(path + "\\" + name)
  62. image_path = path + "\\" + name + "\\"
  63. ## 开始处理
  64. i = 1 # 从一开始
  65. list_name = os.listdir(pathorg) # 获取图片名称列表
  66. for item in list_name:
  67. # 检查是否有图片
  68. tage = os.path.exists(pathorg + str(i) + '.png')
  69. if not tage:
  70. image = Image.open(pathorg + '\\' + item)
  71. std = image.resize((width, length), Image.ANTIALIAS)
  72. ## 模式为RGB
  73. if not std.mode == "RGB":
  74. std = std.convert('RGB')
  75. std.save(image_path + str(i) + '.png')
  76. i += 1
  77. def label_init(lable):
  78. n = lable.shape[0]
  79. label_Y = np.zeros([10, n])
  80. res = lable.astype(int)
  81. for i in range(0, label_Y.shape[1]):
  82. label_Y[res[i], i] = 1
  83. return label_Y
  84. def get_X(path):
  85. im_name_list = os.listdir(path)
  86. all_data = []
  87. for item in im_name_list:
  88. try:
  89. all_data.append(plt.imread(path + '\\' + item).tolist())
  90. except:
  91. print(item + " open error ")
  92. return all_data
'
运行

(2)DeepNet.py

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import datetime
  4. import os
  5. import sys
  6. import h5py
  7. # sigmoid
  8. def sigmoid(Z):
  9. A = 1/(1+np.exp(-Z))
  10. cache = Z
  11. return A, cache
  12. def sigmoid_backward(dA, cache):
  13. Z = cache
  14. s = 1/(1+np.exp(-Z))
  15. dZ = dA * s * (1-s)
  16. return dZ
  17. # relu函数 和反向求导
  18. def relu(Z):
  19. A = np.maximum(0,Z)
  20. cache = Z
  21. return A, cache
  22. def relu_backward(dA, cache):
  23. Z = cache
  24. dZ = np.array(dA, copy=True)
  25. dZ[Z <= 0] = 0
  26. return dZ
  27. # Softmax
  28. def softmax(Z):
  29. A = np.exp(Z)/np.sum(np.exp(Z),axis=0)
  30. cache = Z
  31. return A, cache
  32. # 初始化w
  33. # def INIT_W(n_x,n_h1,n_h2,n_y):
  34. # W1 = np.random.randn(n_h1, n_x ) * 0.01
  35. # b1 = np.zeros((n_h1, 1))
  36. # W2 = np.random.randn(n_h2,n_h1)*0.01
  37. # b2 = np.zeros((n_h2,1))
  38. # W3 = np.random.randn(n_y, n_h2) * 0.01
  39. # b3 = np.zeros((n_y, 1))
  40. # INIT = {
  41. # "W1" : W1,
  42. # "b1" : b1,
  43. # "W2" : W2,
  44. # "b2" : b2,
  45. # "W3" : W3,
  46. # "b3" : b3
  47. # }
  48. # return INIT
  49. def init_W(layers_dims):
  50. np.random.seed(3)
  51. parameters = {}
  52. L = len(layers_dims)
  53. for l in range(1, L):
  54. parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(layers_dims[l - 1])
  55. parameters["b" + str(l)] = np.zeros((layers_dims[l], 1))
  56. return parameters
  57. # 向前
  58. def L_forword_sum(W,A,b):
  59. Z = np.dot(W,A)+b
  60. cache = (A,W,b)
  61. return Z,cache
  62. def L_activate_forworld(A_prev,W,b,activation):
  63. if activation == "relu":
  64. Z ,linear_cache = L_forword_sum(W,A_prev,b)
  65. A, activation_cache = relu(Z)
  66. elif activation == "sigmoid":
  67. Z, linear_cache = L_forword_sum(W, A_prev, b)
  68. A, activation_cache = sigmoid(Z)
  69. elif activation == "softmax":
  70. Z, linear_cache = L_forword_sum(W, A_prev, b)
  71. A, activation_cache = softmax(Z)
  72. cache = (linear_cache, activation_cache)
  73. return A,cache
  74. def L_forword(X, parameters):
  75. caches = []
  76. A = X
  77. L = len(parameters) // 2
  78. for l in range(1, L):
  79. A_prev = A
  80. A, cache = L_activate_forworld(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], "relu")
  81. caches.append(cache)
  82. # 最后一层使用softmax
  83. AL, cache = L_activate_forworld(A, parameters['W' + str(L)], parameters['b' + str(L)], "softmax")
  84. caches.append(cache)
  85. return AL, caches
  86. #计算代价
  87. def cost(Y_out,Y):
  88. cost = -np.sum(np.multiply(np.log(Y_out), Y)) / Y_out.shape[1]
  89. cost = np.squeeze(cost)
  90. return cost
  91. #线性返回
  92. def linear_backward(dZ, cache):
  93. A_prev, W, b = cache
  94. m = A_prev.shape[1]
  95. dW = np.dot(dZ, A_prev.T) / m
  96. db = np.sum(dZ, axis=1, keepdims=True) / m
  97. dA_prev = np.dot(W.T, dZ)
  98. return dA_prev, dW, db
  99. def linear_activation_backward(dA, cache, Y,activation="relu"):
  100. linear_cache, activation_cache = cache
  101. if activation == "relu":
  102. dZ = relu_backward(dA, activation_cache)
  103. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  104. elif activation == "sigmoid":
  105. dZ = sigmoid_backward(dA, activation_cache)
  106. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  107. elif activation == "softmax":
  108. dZ = dA - Y
  109. dA_prev, dW, db = linear_backward(dZ, linear_cache)
  110. return dA_prev, dW, db
  111. def L_model_backward(AL, Y, caches,case):
  112. grads = {}
  113. L = len(caches)
  114. m = AL.shape[1]
  115. Y = Y.reshape(AL.shape)
  116. dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
  117. if case == "softmax":
  118. current_cache = caches[L - 1]
  119. grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y,"softmax")
  120. elif case == "sigmoid":
  121. current_cache = caches[L - 1]
  122. grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y, "sigmoid")
  123. for l in reversed(range(L - 1)):
  124. current_cache = caches[l]
  125. dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, Y ,"relu")
  126. grads["dA" + str(l + 1)] = dA_prev_temp
  127. grads["dW" + str(l + 1)] = dW_temp
  128. grads["db" + str(l + 1)] = db_temp
  129. return grads
  130. def update_parameters(parameters, grads, learning_rate):
  131. L = len(parameters) // 2 # 整除
  132. for l in range(L):
  133. parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
  134. parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
  135. return parameters
  136. def deepnet(X, Y,net_layers,learning_rate=0.0075, num_iterations=3000,step =1, print_cost=False, isPlot=True):
  137. np.random.seed(1) #设计种子
  138. costs = [] #用于画图
  139. parameters = init_W(net_layers)
  140. for i in range(0, num_iterations):
  141. # 迭代
  142. AL, caches = L_forword(X, parameters)
  143. costi = cost(AL, Y) #这里的Y是标准化的Y
  144. grads = L_model_backward(AL, Y, caches,"softmax")
  145. parameters = update_parameters(parameters, grads, learning_rate)
  146. if i % step == 0:
  147. # 记录成本
  148. costs.append(costi)
  149. # 是否打印成本值
  150. if print_cost:
  151. print("第", i, "次迭代,成本值为:", np.squeeze(costi))
  152. if isPlot:
  153. plt.plot(np.squeeze(costs))
  154. plt.ylabel('cost')
  155. plt.xlabel('iterations (per tens)')
  156. plt.title("Learning rate =" + str(learning_rate))
  157. plt.show()
  158. # plt.savefig(fnme = "cast"+str(datetime.datetime.now())+".jig")
  159. return parameters
  160. def predict(X, y, parameters,Y_org):
  161. m = X.shape[1]
  162. n = len(parameters) // 2 # 神经网络的层数
  163. p = np.zeros((1, m))
  164. # 根据参数前向传播
  165. probas, caches = L_forword(X, parameters)
  166. p = np.argmax(probas,axis=0)
  167. zql = float(np.sum((p == Y_org)) / m)
  168. print("准确度为: " + str(float(np.sum((p == Y_org)) / m)))
  169. error_list = []
  170. for i in range(m):
  171. if p[i] != Y_org[i]:
  172. error_list.append(i)
  173. return p,error_list,zql
  174. def save_model(parameters):
  175. np.set_printoptions(threshold=sys.maxsize)
  176. model_number = 0
  177. f = open("model/model" + str(model_number) + ".txt", "a+")
  178. f.write(str(datetime.datetime.now()) + "\n")
  179. f.write("model_number " + str(model_number) + "\n")
  180. for i, j in parameters.items():
  181. f.write(str(i) + "\n")
  182. f.write(str(j) + "\n")
  183. f.close()
  184. return 0
  185. #保存为h5数据格式
  186. def save_h5(data,layers,zql):
  187. str1 = "./model/model"+str(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))+".h5"
  188. f = h5py.File(str1, "w")
  189. ID = ["model layer "]
  190. f.create_dataset("layers",data = layers)
  191. i = len(data) // 2
  192. for j in range(i):
  193. f.create_dataset("W"+str(j+1),data = data["W"+str(j+1)])
  194. f.create_dataset("b"+str(j+1),data = data["b"+str(j+1)])
  195. f.create_dataset("accuracy",data = zql)
  196. f.close()
  197. def predict1(X, parameters):
  198. # 根据参数前向传播
  199. probas, caches = L_forword(X, parameters)
  200. p = np.argmax(probas,axis=0)
  201. return p
  202. def read_ccs(path):
  203. w = h5py.File(path, "r")
  204. layers = w["layers"][:]
  205. l = len(layers)
  206. p = {}
  207. # print(l)
  208. for i in range(1, l):
  209. p["W" + str(i)] = w["W" + str(i)][:]
  210. p["b" + str(i)] = w["b" + str(i)][:]
  211. return p, layers

(3)train.py

  1. import DeepNET
  2. import time
  3. import function
  4. if __name__ == '__main__':
  5. # 计时开始
  6. start1 = time.time()
  7. train_image_path = './MNIST/train-images-idx3-ubyte/train-images.idx3-ubyte'
  8. train_lable_path = './MNIST/train-labels-idx1-ubyte/train-labels.idx1-ubyte'
  9. teat_image_path = './MNIST/t10k-images-idx3-ubyte/t10k-images.idx3-ubyte'
  10. teat_lable_path = './MNIST/t10k-labels-idx1-ubyte/t10k-labels.idx1-ubyte'
  11. # #加载数据
  12. train_image = function.jiexi_image(train_image_path)
  13. train_lable = function.jiexi_label(train_lable_path)
  14. teat_image = function.jiexi_image(teat_image_path)
  15. test_lable = function.jiexi_label(teat_lable_path)
  16. # print(train_image.shape)
  17. function.plot_data(train_image,train_lable,10,True)
  18. train_image = train_image.reshape(train_image.shape[0], -1).T / 255
  19. teat_image = teat_image.reshape(teat_image.shape[0], -1).T / 255
  20. train_lable1 = function.label_init(train_lable)
  21. test_lable1 = function.label_init(test_lable)
  22. print(train_image.shape)
  23. end1 = time.time()
  24. start2 = time.time()
  25. layers = [784, 200, 150, 10]
  26. parameters = DeepNET.deepnet(train_image, train_lable1,layers , learning_rate=0.0075, num_iterations=3000,
  27. step=100, print_cost=True, isPlot=True)
  28. end2 = time.time()
  29. p ,error_list_train,zql1 = DeepNET.predict(train_image, train_lable1, parameters, train_lable)
  30. p0 ,error_list_test ,zql2 = DeepNET.predict(teat_image,test_lable1,parameters,test_lable)
  31. zql = [[zql1],[zql2]]
  32. print("数据加载时间:",end1-start1," 秒")
  33. print("模型训练时间:",end2-start2," 秒")
  34. DeepNET.save_h5(parameters,layers,zql)

(4) main.py

  1. # 加载库
  2. from MainWidget import MainWidget
  3. from PyQt5.QtWidgets import QApplication
  4. import sys
  5. def main():
  6. app = QApplication(sys.argv)
  7. mainWidget = MainWidget() # 新建一个主界面
  8. mainWidget.show() # 显示主界面
  9. exit(app.exec_()) # 进入消息循环
  10. if __name__ == '__main__':
  11. main()

(5)mainwidget.py

使用画板程序之前得跟该你的模型路径名字。就是红色部分。

def yuce(self):
        # #标准化图片 获取Y
        savePath = "./image_rgzn/test.png"
        image = self.__paintBoard.GetContentAsQImage()
        image.save(savePath)
        img = Image.open(savePath)
        img = img.convert("I")
        img = img.resize((28, 28))
        x = np.array(img)
        train_image = x.reshape(1, -1).T / 255
        w,layer = DeepNET.read_ccs("./model/model20221119225104.h5")
        p = DeepNET.predict1(train_image,w)
        self.__text_out.setText(str(p[0]))
        print(p)
        # print("hello")
        # res = QMessageBox.information(self,"人工智能判断为:",str(p),QMessageBox.Yes|QMessageBox.No)
        # res.exec_()
        # 读取数据权重

        # 预测并输出

  1. '''
  2. Created on 2018年8月8日
  3. @author: Freedom
  4. '''
  5. from PyQt5.Qt import QWidget, QColor, QPixmap, QIcon, QSize, QCheckBox
  6. from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QSplitter, \
  7. QComboBox, QLabel, QSpinBox, QFileDialog,QTextEdit
  8. from PaintBoard import PaintBoard
  9. import numpy as np
  10. from PIL import Image
  11. import DeepNET
  12. class MainWidget(QWidget):
  13. def __init__(self, Parent=None):
  14. '''
  15. Constructor
  16. '''
  17. super().__init__(Parent)
  18. self.__InitData() # 先初始化数据,再初始化界面
  19. self.__InitView()
  20. def __InitData(self):
  21. '''
  22. 初始化成员变量
  23. '''
  24. self.__paintBoard = PaintBoard(self)
  25. # 获取颜色列表(字符串类型)
  26. self.__colorList = QColor.colorNames()
  27. def __InitView(self):
  28. '''
  29. 初始化界面
  30. '''
  31. self.setFixedSize(640, 480)
  32. self.setWindowTitle("PaintBoard Example PyQt5")
  33. # 新建一个水平布局作为本窗体的主布局
  34. main_layout = QHBoxLayout(self)
  35. # 设置主布局内边距以及控件间距为10px
  36. main_layout.setSpacing(10)
  37. # 在主界面左侧放置画板
  38. main_layout.addWidget(self.__paintBoard)
  39. # 新建垂直子布局用于放置按键
  40. sub_layout = QVBoxLayout()
  41. # 设置此子布局和内部控件的间距为10px
  42. sub_layout.setContentsMargins(10, 10, 10, 10)
  43. self.__btn_Clear = QPushButton("清空画板")
  44. self.__btn_Clear.setParent(self) # 设置父对象为本界面
  45. # 将按键按下信号与画板清空函数相关联
  46. self.__btn_Clear.clicked.connect(self.__paintBoard.Clear)
  47. sub_layout.addWidget(self.__btn_Clear)
  48. self.__btn_yuce = QPushButton("人工智能预测")
  49. self.__btn_yuce.setParent(self) # 设置父对象为本界面
  50. self.__btn_yuce.clicked.connect(lambda:self.yuce())
  51. sub_layout.addWidget(self.__btn_yuce)
  52. self.__text_out = QTextEdit(self)
  53. self.__text_out.setParent(self)
  54. self.__text_out.setObjectName("预测结果为:")
  55. sub_layout.addWidget(self.__text_out)
  56. self.__btn_Quit = QPushButton("退出")
  57. self.__btn_Quit.setParent(self) # 设置父对象为本界面
  58. self.__btn_Quit.clicked.connect(self.Quit)
  59. sub_layout.addWidget(self.__btn_Quit)
  60. self.__btn_Save = QPushButton("保存作品")
  61. self.__btn_Save.setParent(self)
  62. self.__btn_Save.clicked.connect(self.on_btn_Save_Clicked)
  63. sub_layout.addWidget(self.__btn_Save)
  64. self.__cbtn_Eraser = QCheckBox(" 使用橡皮擦")
  65. self.__cbtn_Eraser.setParent(self)
  66. self.__cbtn_Eraser.clicked.connect(self.on_cbtn_Eraser_clicked)
  67. sub_layout.addWidget(self.__cbtn_Eraser)
  68. splitter = QSplitter(self) # 占位符
  69. sub_layout.addWidget(splitter)
  70. self.__label_penThickness = QLabel(self)
  71. self.__label_penThickness.setText("画笔粗细")
  72. self.__label_penThickness.setFixedHeight(20)
  73. sub_layout.addWidget(self.__label_penThickness)
  74. self.__spinBox_penThickness = QSpinBox(self)
  75. self.__spinBox_penThickness.setMaximum(40)
  76. self.__spinBox_penThickness.setMinimum(2)
  77. self.__spinBox_penThickness.setValue(20) # 默认粗细为10
  78. self.__spinBox_penThickness.setSingleStep(2) # 最小变化值为2
  79. self.__spinBox_penThickness.valueChanged.connect(
  80. self.on_PenThicknessChange) # 关联spinBox值变化信号和函数on_PenThicknessChange
  81. sub_layout.addWidget(self.__spinBox_penThickness)
  82. self.__label_penColor = QLabel(self)
  83. self.__label_penColor.setText("画笔颜色")
  84. self.__label_penColor.setFixedHeight(20)
  85. sub_layout.addWidget(self.__label_penColor)
  86. self.__comboBox_penColor = QComboBox(self)
  87. self.__fillColorList(self.__comboBox_penColor) # 用各种颜色填充下拉列表
  88. self.__comboBox_penColor.currentIndexChanged.connect(
  89. self.on_PenColorChange) # 关联下拉列表的当前索引变更信号与函数on_PenColorChange
  90. sub_layout.addWidget(self.__comboBox_penColor)
  91. main_layout.addLayout(sub_layout) # 将子布局加入主布局
  92. def __fillColorList(self, comboBox):
  93. index_black = 0
  94. index = 0
  95. for color in self.__colorList:
  96. if color == "black":
  97. index_black = index
  98. index += 1
  99. pix = QPixmap(70, 20)
  100. pix.fill(QColor(color))
  101. comboBox.addItem(QIcon(pix), None)
  102. comboBox.setIconSize(QSize(70, 20))
  103. comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
  104. comboBox.setCurrentIndex(index_black)
  105. def on_PenColorChange(self):
  106. color_index = self.__comboBox_penColor.currentIndex()
  107. color_str = self.__colorList[color_index]
  108. self.__paintBoard.ChangePenColor(color_str)
  109. def on_PenThicknessChange(self):
  110. penThickness = self.__spinBox_penThickness.value()
  111. self.__paintBoard.ChangePenThickness(penThickness)
  112. def on_btn_Save_Clicked(self):
  113. savePath = QFileDialog.getSaveFileName(self, 'Save Your Paint', '.\\', '*.png')
  114. print(savePath)
  115. if savePath[0] == "":
  116. print("Save cancel")
  117. return
  118. image = self.__paintBoard.GetContentAsQImage()
  119. image.save(savePath[0])
  120. def on_cbtn_Eraser_clicked(self):
  121. if self.__cbtn_Eraser.isChecked():
  122. self.__paintBoard.EraserMode = True # 进入橡皮擦模式
  123. else:
  124. self.__paintBoard.EraserMode = False # 退出橡皮擦模式
  125. def Quit(self):
  126. self.close()
  127. def yuce(self):
  128. # #标准化图片 获取Y
  129. savePath = "./image_rgzn/test.png"
  130. image = self.__paintBoard.GetContentAsQImage()
  131. image.save(savePath)
  132. img = Image.open(savePath)
  133. img = img.convert("I")
  134. img = img.resize((28, 28))
  135. x = np.array(img)
  136. train_image = x.reshape(1, -1).T / 255
  137. w,layer = DeepNET.read_ccs("./model/model20221119225104.h5")
  138. p = DeepNET.predict1(train_image,w)
  139. self.__text_out.setText(str(p[0]))
  140. print(p)
  141. # print("hello")
  142. # res = QMessageBox.information(self,"人工智能判断为:",str(p),QMessageBox.Yes|QMessageBox.No)
  143. # res.exec_()
  144. # 读取数据权重
  145. # 预测并输出

(6)paintboard.py

  1. '''
  2. Created on 2018年8月9日
  3. @author: Freedom
  4. '''
  5. from PyQt5.QtWidgets import QWidget
  6. from PyQt5.Qt import QPixmap, QPainter, QPoint, QPaintEvent, QMouseEvent, QPen, \
  7. QColor, QSize
  8. from PyQt5.QtCore import Qt
  9. class PaintBoard(QWidget):
  10. def __init__(self, Parent=None):
  11. '''
  12. Constructor
  13. '''
  14. super().__init__(Parent)
  15. self.__InitData() # 先初始化数据,再初始化界面
  16. self.__InitView()
  17. def __InitData(self):
  18. self.__size = QSize(280, 280)
  19. # 新建QPixmap作为画板,尺寸为__size
  20. self.__board = QPixmap(self.__size)
  21. self.__board.fill(Qt.black) # 用白色填充画板
  22. self.__IsEmpty = True # 默认为空画板
  23. self.EraserMode = False # 默认为禁用橡皮擦模式
  24. self.__lastPos = QPoint(0, 0) # 上一次鼠标位置
  25. self.__currentPos = QPoint(0, 0) # 当前的鼠标位置
  26. self.__painter = QPainter() # 新建绘图工具
  27. self.__thickness = 10 # 默认画笔粗细为10px
  28. self.__penColor = QColor("white") # 设置默认画笔颜色为黑色
  29. self.__colorList = QColor.colorNames() # 获取颜色列表
  30. def __InitView(self):
  31. # 设置界面的尺寸为__size
  32. self.setFixedSize(self.__size)
  33. def Clear(self):
  34. # 清空画板
  35. self.__board.fill(Qt.black)
  36. self.update()
  37. self.__IsEmpty = True
  38. def ChangePenColor(self, color="black"):
  39. # 改变画笔颜色
  40. self.__penColor = QColor(color)
  41. def ChangePenThickness(self, thickness=10):
  42. # 改变画笔粗细
  43. self.__thickness = thickness
  44. def IsEmpty(self):
  45. # 返回画板是否为空
  46. return self.__IsEmpty
  47. def GetContentAsQImage(self):
  48. # 获取画板内容(返回QImage)
  49. image = self.__board.toImage()
  50. return image
  51. def paintEvent(self, paintEvent):
  52. # 绘图事件
  53. # 绘图时必须使用QPainter的实例,此处为__painter
  54. # 绘图在begin()函数与end()函数间进行
  55. # begin(param)的参数要指定绘图设备,即把图画在哪里
  56. # drawPixmap用于绘制QPixmap类型的对象
  57. self.__painter.begin(self)
  58. # 0,0为绘图的左上角起点的坐标,__board即要绘制的图
  59. self.__painter.drawPixmap(0, 0, self.__board)
  60. self.__painter.end()
  61. def mousePressEvent(self, mouseEvent):
  62. # 鼠标按下时,获取鼠标的当前位置保存为上一次位置
  63. self.__currentPos = mouseEvent.pos()
  64. self.__lastPos = self.__currentPos
  65. def mouseMoveEvent(self, mouseEvent):
  66. # 鼠标移动时,更新当前位置,并在上一个位置和当前位置间画线
  67. self.__currentPos = mouseEvent.pos()
  68. self.__painter.begin(self.__board)
  69. if self.EraserMode == False:
  70. # 非橡皮擦模式
  71. self.__painter.setPen(QPen(self.__penColor, self.__thickness)) # 设置画笔颜色,粗细
  72. else:
  73. # 橡皮擦模式下画笔为纯白色,粗细为10
  74. self.__painter.setPen(QPen(Qt.white, 10))
  75. # 画线
  76. self.__painter.drawLine(self.__lastPos, self.__currentPos)
  77. self.__painter.end()
  78. self.__lastPos = self.__currentPos
  79. self.update() # 更新显示
  80. def mouseReleaseEvent(self, mouseEvent):
  81. self.__IsEmpty = False # 画板不再为空
'
运行

        感谢看完的读者,希望你们都可以对编程产生热爱!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/901168
推荐阅读
相关标签
  

闽ICP备14008679号