# 社区诊所排队叫号识别 **Repository Path**: ltdwh/CommunityClinicQueuing ## Basic Information - **Project Name**: 社区诊所排队叫号识别 - **Description**: 社区诊所坐落在社区附近,面向整个社区为居民提供基础问诊治疗服务,社区诊所往往高峰期就诊量较大,容易产生就诊人数过多且病患无法在现场排队的情况,导致就诊居民无法及时了解到现场排队情况,因而设计通过智能识别当前就诊号图像信息再转为微信消息即实现居民在家排队等候就诊功能,可以解决高峰期就诊人数过多和现场排队环境狭小的矛盾,可以为医生和患者提供一个良好的诊疗环境,具有较好的社会意义。 - **Primary Language**: Python - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-02-28 - **Last Updated**: 2022-04-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、项目背景 社区诊所坐落在社区附近,面向整个社区为居民提供基础问诊治疗服务,是社会医疗系统的重要补充部分。在社区诊所就诊的大部分是社区儿童,通常就诊时由1-2个大人携带1名小朋友前来就诊,且往往高峰期就诊量较大,而社区诊所往往面积较小,可以同时容纳的就诊人数较少,从而产生了就诊人数过多且病患无法在现场排队的情况,导致就诊居民无法及时了解到现场排队情况,过早或过迟就诊导致现场排队混乱,容易引发纠纷。通过现场环境分析发现诊所采用了一块LED数码管来显示排队就诊号信息,因而设计通过智能识别当前就诊号图像信息再转为微信消息即实现居民在家排队等候就诊功能,可以解决高峰期就诊人数过多和现场排队环境狭小的矛盾,可以为医生和患者提供一个良好的诊疗环境,具有较好的社会意义。 ## 二、数据集介绍 本项目使用的数据集照片来自公共数据集中的印刷数字和部分数码管数字数据集。包含了不同数字的图片,该数据集的数据量不多,需要适当通过一些处理来增加训练集数据量。 [印刷数字数据集](https://aistudio.baidu.com/aistudio/datasetdetail/49817) ![](https://ai-studio-static-online.cdn.bcebos.com/9d59d13ec997477e9955dc943e7953806d661d6adda844708ac64cf824e48a21) ## 三、准备数据 import sys sys.path.append('/home/aistudio/external-libraries') import zipfile import os #解压数据集 def unzip_file(zip_src, dst_dir): if not os.path.exists(dst_dir+'mnist'): r = zipfile.is_zipfile(zip_src) if r: fz = zipfile.ZipFile(zip_src, 'r') for file in fz.namelist(): fz.extract(file, dst_dir) print("解压成功") else: print('文件类型错误') unzip_file('/home/aistudio/data/data49817/dataset.zip','/home/aistudio/') #导入需要的包 import numpy as np import paddle as paddle import paddle.fluid as fluid from PIL import Image import matplotlib.pyplot as plt import os def load_image(img_path): im = Image.open(img_path) #im = im.resize((28, 28), Image.ANTIALIAS) im = np.array(im).reshape(1, -1).astype(np.float32) # 图像归一化,保持和数据集的数据范围一致 im = 1 - im / 127.5 return im def get_data(data_type): def generate_data(): path = './mnist/'+data_type+'.txt' with open(path, 'r') as f: for line in f: if line != "\n": image_path, label = line.strip().split(' ') yield (load_image(image_path)[0], label) return generate_data train_dataset = get_data('train') test_dataset = get_data('test') BUF_SIZE=512 BATCH_SIZE=128 #print(load_image('./dataset/test/0/0.png')) #用于训练的数据提供器,每次从缓存中随机读取批次大小的数据 train_reader = paddle.batch( paddle.reader.shuffle(train_dataset, buf_size=BUF_SIZE), batch_size=BATCH_SIZE) #用于训练的数据提供器,每次从缓存中随机读取批次大小的数据 test_reader = paddle.batch( paddle.reader.shuffle(test_dataset, buf_size=BUF_SIZE), batch_size=BATCH_SIZE) ## 四、模型介绍 a) PaddleHub提供了以下文字识别预训练模型: 移动端的超轻量模型:仅有8.1M,chinese_ocr_db_crnn_mobile。 服务器端的精度更高模型:识别精度更高,chinese_ocr_db_crnn_server。 模型目前支持「身份证识别」、「火车票识别」、「快递单识别」、「广告信息识别」、「网络图片文字识别」这五个主要场景,预测效果比较好。 识别文字算法均采用CRNN(Convolutional Recurrent Neural Network)即卷积递归神经网络。其是DCNN和RNN的组合,专门用于识别图像中的序列式对象。与CTC loss配合使用,进行文字识别,可以直接从文本词级或行级的标注中学习,不需要详细的字符级的标注。该Module支持直接预测。 移动端与服务器端主要在于骨干网络的差异性,移动端采用MobileNetV3,服务器端采用ResNet50_vd。 chinese_ocr_db_crnn_server Module用于识别图片当中的汉字。其基于chinese_text_detection_db_server检测得到的文本框,继续识别文本框中的中文文字。之后对检测文本框进行角度分类。最终识别文字算法采用CRNN(Convolutional Recurrent Neural Network)即卷积递归神经网络。其是DCNN和RNN的组合,专门用于识别图像中的序列式对象。与CTC loss配合使用,进行文字识别,可以直接从文本词级或行级的标注中学习,不需要详细的字符级的标注。该Module是一个通用的OCR模型,支持直接预测。 RNN的网络结构图: ![image.png](https://ai-studio-static-online.cdn.bcebos.com/af68e45eea184b4c966f23ad7d9fd295e07e1fc31cc74134b4bd99ee275bed63)! b) 或者搭建LeNet卷积神经网络 VGG是当前最流行的CNN模型之一,2014年由Simonyan和Zisserman发表在ICLR 2015会议上的论文《Very Deep Convolutional Networks For Large-scale Image Recognition》提出,其命名来源于论文作者所在的实验室Visual Geometry Group。VGG设计了一种大小为3x3的小尺寸卷积核和池化层组成的基础模块,通过堆叠上述基础模块构造出深度卷积神经网络,该网络在图像分类领域取得了不错的效果,在大型分类数据集ILSVRC上,VGG模型仅有6.8% 的top-5 test error 。VGG模型一经推出就很受研究者们的欢迎,因为其网络结构的设计合理,总体结构简明,且可以适用于多个领域。VGG的设计为后续研究者设计模型结构提供了思路。 下图是VGG-16的网络结构示意图,一共包含13层卷积和3层全连接层。VGG网络使用3×3的卷积层和池化层组成的基础模块来提取特征,三层全连接层放在网络的最后组成分类器,最后一层全连接层的输出即为分类的预测。 在VGG中每层卷积将使用ReLU作为激活函数,在全连接层之后添加dropout来抑制过拟合。使用小的卷积核能够有效地减少参数的个数,使得训练和测试变得更加有效。比如如果我们想要得到感受野为5的特征图,最直接的方法是使用5×5的卷积层,但是我们也可以使用两层3×3卷积层达到同样的效果,并且只需要更少的参数。另外由于卷积核比较小,我们可以堆叠更多的卷积层,提取到更多的图片信息,来提高图像分类的准确率。VGG模型的成功证明了增加网络的深度,可以更好的学习图像中的特征模式,达到更高的分类准确率。 ![image.png](https://ai-studio-static-online.cdn.bcebos.com/3b6e1725e5934d2293e03b9c0a83e1d48660137f3c4449ba89bf9766d4380f3a) AlexNet模型由Alex Krizhevsky、Ilya Sutskever和Geoffrey E. Hinton开发,是2012年ImageNet挑战赛冠军模型。相比于LeNet模型,AlexNet的神经网络层数更多,其中包含ReLU激活层,并且在全连接层引入Dropout机制防止过拟合。下面详细解析AlexNet模型。 ![image.png](https://ai-studio-static-online.cdn.bcebos.com/f7e2bb751ad944d9b4c6a8e2663bfeaed46c886f81f44e86ab5f63f2d755ba11) ## 五、网络配置 def LeNet(input): #输出图像为 conv1 = fluid.layers.conv2d(input=input, num_filters=6, filter_size=5, act='relu') print(conv1.shape) pool1 = fluid.layers.pool2d(input=conv1,pool_size=2, pool_stride=2, pool_type='max') print(pool1.shape) conv2 = fluid.layers.conv2d(input=pool1, num_filters=16, filter_size=5, act='relu') print(conv2.shape) pool2 = fluid.layers.pool2d(input=conv2,pool_size=2, pool_stride=2, pool_type='max') # 创建第3个卷积层 print(pool2.shape) conv3 = fluid.layers.conv2d(input=pool2, num_filters=120, filter_size=4, act='relu') # 创建全连接层,第一个全连接层的输出神经元个数为64, 第二个全连接层输出神经元个数为分裂标签的类别数 print(conv3.shape) fc1 = fluid.layers.fc(input=conv3,size=84, act=None) fc2 = fluid.layers.fc(input=fc1,size=10,act='softmax') return fc2 #定义一个VGG16 def VGG_network(input): conv1 = fluid.layers.conv2d(input=input, num_filters=128, filter_size=3, stride=1, padding=3, act='relu') print(conv1.shape) pool1 = fluid.layers.pool2d(input=conv1, pool_size=2, pool_stride=2, pool_type='max') print(pool1.shape) conv2 = fluid.layers.conv2d(input=pool1, num_filters=256, filter_size=3, stride=1, padding=1, act='relu') print(conv2.shape) pool2 = fluid.layers.pool2d(input=conv2, pool_size=2, pool_stride=2, pool_type='max') print(pool2.shape) conv3 = fluid.layers.conv2d(input=pool2, num_filters=768, filter_size=3, stride=1, padding=1, act='relu') print(conv3.shape)def LeNet(input): #输出图像为 conv1 = fluid.layers.conv2d(input=input, num_filters=6, filter_size=5, act='relu') print(conv1.shape) pool1 = fluid.layers.pool2d(input=conv1,pool_size=2, pool_stride=2, pool_type='max') print(pool1.shape) conv2 = fluid.layers.conv2d(input=pool1, num_filters=16, filter_size=5, act='relu') print(conv2.shape) pool2 = fluid.layers.pool2d(input=conv2,pool_size=2, pool_stride=2, pool_type='max') # 创建第3个卷积层 print(pool2.shape) conv3 = fluid.layers.conv2d(input=pool2, num_filters=120, filter_size=4, act='relu') # 创建全连接层,第一个全连接层的输出神经元个数为64, 第二个全连接层输出神经元个数为分裂标签的类别数 print(conv3.shape) fc1 = fluid.layers.fc(input=conv3,size=84, act=None) fc2 = fluid.layers.fc(input=fc1,size=10,act='softmax') return fc2 pool3 = fluid.layers.pool2d(input=conv3, pool_size=2, pool_stride=2, pool_type='max') print(pool3.shape) conv4 = fluid.layers.conv2d(input=pool3, num_filters=1536, filter_size=3, stride=1, padding=1, act='relu') print(conv4.shape) pool4 = fluid.layers.pool2d(input=conv4, pool_size=2, pool_stride=2, pool_type='max') print(pool4.shape) conv5 = fluid.layers.conv2d(input=pool4, num_filters=1536, filter_size=3, stride=1, padding=1, act='relu') print(conv5.shape) pool5 = fluid.layers.pool2d(input=conv5, pool_size=2, pool_stride=2, pool_type='max') print(pool5.shape) drop = fluid.layers.dropout(x=pool5, dropout_prob=0.5) fc1 = fluid.layers.fc(input=drop, size=256, act=None) bn = fluid.layers.batch_norm(input=fc1, act='relu') drop2 = fluid.layers.dropout(x=bn, dropout_prob=0.0) fc2 = fluid.layers.fc(input=drop2, size=256, act=None) fc3 = fluid.layers.fc(input=fc2, size=10, act='softmax') print(fc3.shape) return fc3 #定义一个AlexNet def Alex_network(input): #输出为(28-11+2*5)/4+1= 7, 7*7*96 conv1 = fluid.layers.conv2d(input=input, num_filters=96, filter_size=11, stride=4, padding=5, act='relu') print(conv1.shape) #输出为3*3*96 pool1 = fluid.layers.pool2d(input=conv1, pool_size=2, pool_stride=2, pool_type='max') print(pool1.shape) #输出为3*3*256 conv2 = fluid.layers.conv2d(input=pool1, num_filters=256, filter_size=5, stride=1, padding=2, act='relu') print(conv2.shape) #输出为1*1*256 pool2 = fluid.layers.pool2d(input=conv2, pool_size=2, pool_stride=2, pool_type='max') #print(pool2.shape) #输出为1*1*384 conv3 = fluid.layers.conv2d(input=pool2, num_filters=384, filter_size=3, stride=1, padding=1, act='relu') print(conv3.shape) #输出为1*1*384 conv4 = fluid.layers.conv2d(input=conv3, num_filters=384, filter_size=3, stride=1, padding=1, act='relu') print(conv4.shape) #输出为1*1*384 conv5 = fluid.layers.conv2d(input=conv4, num_filters=256, filter_size=3, stride=1, padding=1, act='relu') print(conv5.shape) #输出为1*1*384 pool3 = fluid.layers.pool2d(input=conv5, pool_size=2, pool_stride=2, pool_type='max') print(pool3.shape) dropout = fluid.layers.dropout(x=pool3,dropout_prob=0.5) print(dropout.shape) #256 fc1 = fluid.layers.fc(input=dropout, size=4096, act='relu') print(fc1.shape) fc2 = fluid.layers.fc(input=fc1, size=4096, act='relu') print(fc2.shape) #256 dropout2 = fluid.layers.dropout(x=fc2,dropout_prob=0.5) print(dropout2.shape) #10 fc3 = fluid.layers.fc(input=dropout2, size=10,act='softmax') print(fc3.shape) return fc3 #输入的原始图像数据,大小为1*28*28 image = fluid.layers.data(name='image', shape=[1, 28, 28], dtype='float32')#3通道,28*28像素值 #标签,名称为label,对应输入图片的类别标签 label = fluid.layers.data(name='label', shape=[1], dtype='int64') #图片标签 #vgg分类器 predict = VGG_network(image) #使用交叉熵损失函数,描述真实样本标签和预测概率之间的差值 #cost = fluid.layers.softmax_with_cross_entropy(logits=predict, label=label) cost = fluid.layers.cross_entropy(input=predict, label=label) #使用类交叉熵函数计算predict和label之间的损失函数 avg_cost = fluid.layers.mean(cost) #计算分类准确率 acc = fluid.layers.accuracy(input=predict, label=label) #使用Adam算法进行优化, learning_rate 是学习率(它的大小与网络的训练收敛速度有关系) #optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.001) #optimizer = fluid.optimizer.Adagrad(learning_rate=0.001) #optimizer = fluid.optimizer.RMSProp(learning_rate=0.001) #optimizer = fluid.optimizer.Adadelta(learning_rate=0.001) optimizer = fluid.optimizer.FtrlOptimizer(learning_rate=0.001) #optimizer = fluid.optimizer.FtrlOptimizer(learning_rate=0.0001) #optimizer = fluid.optimizer.FtrlOptimizer(learning_rate=0.01) #optimizer = fluid.optimizer.FtrlOptimizer(learning_rate=0.1) #optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=0.005, momentum=0.9, use_nesterov=True) #optimizer = fluid.optimizer.Adamax(learning_rate=0.001) #optimizer = fluid.optimizer.DecayedAdagrad(learning_rate=0.001) opts = optimizer.minimize(avg_cost) ## 六、模型训练 #定义使用CPU还是GPU,使用CPU时use_cuda = False,使用GPU时use_cuda = True use_cuda = True place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() #获取测试程序 test_program = fluid.default_main_program().clone(for_test=True) exe = fluid.Executor(place) exe.run(fluid.default_startup_program()) feeder = fluid.DataFeeder(place=place, feed_list=[image, label]) import seaborn as sns from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt def draw_confusion_matrix(predict_label,actual_label): sns.set() f,ax=plt.subplots(figsize=(8, 8)) C2= confusion_matrix(actual_label, predict_label, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) print(C2) #打印出来看看 sns.heatmap(C2,annot=True,ax=ax,fmt='.20g') #画热力图 ax.set_title('confusion matrix') #标题 ax.set_xlabel('predict') #x轴 ax.set_ylabel('true') #y轴 def draw_train_process(title,iters,costs,accs,label_cost,lable_acc): plt.title(title, fontsize=24) plt.xlabel("iter", fontsize=20) plt.ylabel("cost/acc", fontsize=20) plt.plot(iters, costs,color='red',label=label_cost) plt.plot(iters, accs,color='green',label=lable_acc) plt.legend() plt.grid() plt.show() def draw_cost(title,iters,costs,label_cost): plt.title(title, fontsize=24) plt.xlabel("iter", fontsize=20) plt.ylabel("cost", fontsize=20) plt.plot(iters, costs,color='blue',label=label_cost) plt.legend() plt.grid() plt.show() def draw_acc(title,iters,costs,lable_acc): plt.title(title, fontsize=24) plt.xlabel("iter", fontsize=20) plt.ylabel("acc", fontsize=20) plt.plot(iters, costs,color='blue',label=lable_acc) plt.legend() plt.grid() plt.show() all_train_iter=0 all_train_iters=[] all_train_costs=[] all_train_accs=[] average_train_cost = [] average_test_acc = [] all_test_iter = 0 all_test_iters=[] all_test_costs=[] all_test_accs=[] all_test_predict = [] all_test_acutal = [] EPOCH_NUM=15 model_save_dir = "/home/aistudio/hand.inference.model" for pass_id in range(EPOCH_NUM): # 进行训练 batch = 0 train_costs = [] for batch_id, data in enumerate(train_reader()): #遍历train_reader train_label,train_cost, train_acc = exe.run(program=fluid.default_main_program(),#运行主程序 feed=feeder.feed(data), #给模型喂入数据 fetch_list=[predict, avg_cost, acc]) #fetch 误差、准确率 all_train_iter=all_train_iter+BATCH_SIZE all_train_iters.append(all_train_iter) train_costs.append(train_cost[0]) all_train_costs.append(train_cost[0]) all_train_accs.append(train_acc[0]) batch = batch_id # 每200个batch打印一次信息 误差、准确率 if batch_id % 200 == 0: print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, batch_id, train_cost[0], train_acc[0])) average_train_cost.append(sum(train_costs)/len(train_costs)) print(batch_id) # 进行测试 test_accs = [] test_costs = [] test_iter = 0 #每训练一轮 进行一次测试 for batch_id, data in enumerate(test_reader()): #遍历test_reader test_label, test_cost, test_acc = exe.run(program=test_program, #执行训练程序 feed=feeder.feed(data), #喂入数据 fetch_list=[predict, avg_cost, acc]) #fetch 误差、准确率 test_accs.append(test_acc[0]) #每个batch的准确率 test_costs.append(test_cost[0]) #每个batch的误差 all_test_iter=all_test_iter+BATCH_SIZE all_test_iters.append(all_test_iter) all_test_costs.append(test_cost[0]) all_test_accs.append(test_acc[0]) all_test_predict.append(test_label) all_test_acutal.append([int(d[1]) for d in data]) #求测试结果的平均值 test_cost = (sum(test_costs) / len(test_costs)) #每轮的平均误差 test_acc = (sum(test_accs) / len(test_accs)) #每轮的平均准确率 print('Test:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, test_cost, test_acc)) average_test_acc.append(test_acc) #保存模型 #如果保存路径不存在就创建 if not os.path.exists(model_save_dir): os.makedirs(model_save_dir) print ('save models to %s' % (model_save_dir)) fluid.io.save_inference_model(model_save_dir, #保存推理model的路径 ['image'], #推理(inference)需要 feed 的数据 [predict], #保存推理(inference)结果的 Variables exe) #executor 保存 inference model print('训练模型保存完成!') ## 七、模型评估 print(sum(average_test_acc)/len(average_test_acc)) print(len(average_test_acc)) predict_label = np.array(all_test_predict) actual_label = np.array(all_test_acutal) predict_labels = [] actual_labels = [] for labels in predict_label: for label in labels: predict_labels.append(np.argsort(label)[-1]) for labels in actual_label: for label in labels: actual_labels.append(label) draw_train_process("training",all_train_iters,all_train_costs,all_train_accs,"trainning cost","trainning acc") draw_cost("training",range(len(average_train_cost)),average_train_cost,"trainning cost") draw_acc("testing",range(len(average_test_acc)),average_test_acc,"testing acc") draw_confusion_matrix(predict_labels,actual_labels) print(len(predict_labels)) ## 八、模型预测 #问题解决:上面错误的原因是数据预处理出错,上面数据预处理后的数据维度是(784,), #但实际需要的数据维度是(1, 1, 28, 28), #结构分别是Batch大小,图片通道数,图片的宽和图片的高。所以要通过reshape(1, 1, 28, 28)函数对图片维度进行变换。 def load_image(img_path): im = Image.open(img_path) #将RGB转化为灰度图像,L代表灰度图像,像素值在0~255之间 im = im.resize((28, 28), Image.ANTIALIAS) #resize image with high-quality 图像大小为28*28 im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)#返回新形状的数组,把它变成一个 numpy 数组以匹配数据馈送格式。 # print(im) im = im / 255.0 * 2.0 - 1.0 return im infer_path='mnist/test/1619.png' img = Image.open(infer_path) plt.imshow(img) #根据数组绘制图像 plt.show() #显示图像 infer_exe = fluid.Executor(place) inference_scope = fluid.core.Scope() #加载数据并开始预测 with fluid.scope_guard(inference_scope): #获取训练好的模型 #从指定目录中加载 推理model(inference model) [inference_program, #推理Program feed_target_names, #是一个str列表,它包含需要在推理 Program 中提供数据的变量的名称。 fetch_targets] = fluid.io.load_inference_model(model_save_dir,#fetch_targets:是一个 Variable 列表,从中我们可以得到推断结果。model_save_dir:模型保存的路径 infer_exe) #infer_exe: 运行 inference model的 executor img = load_image(infer_path) results = infer_exe.run(program=inference_program, #运行推测程序 feed={feed_target_names[0]: img}, #喂入要预测的img fetch_list=fetch_targets) #得到推测结果, #获取概率最大的label print(results) lab = np.argsort(results) #argsort函数返回的是result数组值从小到大的索引值 print(lab) print("该图片的预测结果的label为: %d" % lab[0][0][-1]) #-1代表读取数组中倒数第一列