您的位置:首页 > 理论基础 > 计算机网络

2016网络程序设计学习心得-李鹏程-SA16225146

2016-12-29 16:08 513 查看
//摘要:本博客记录了学习孟宁老师的《网络程序设计》过程中的心得以及项目总结

网络程序设计这门课非常有意义。从第一堂课开始,孟宁老师就向我们展示了这学期的研究方向,基于神经网络等机器学习技术实现一个医学辅助诊断的专家系统原型。孟宁老师希望本次项目首先建立对血常规中各项指标的深度学习模型以预测年龄和性别,并最终实现整套web系统。显然,老师的这一想法是极具创新的,寻求血常规指标和性别年龄之间的联系这一研究方向目前并无太多成果,而且传统医生只能通过诊断范围内的经验数据对这一规律进行总结,还未能形成可靠科学的研究成果。基于以上情况下,使用深度学习对这个方向进行研究的重要性不言而喻,由此我们便在孟宁老师的带领下开始了本学期的学习。

项目代码控制:mengning/np2016
课程传送门:NP2016
个人版本库:MTxiaoshutong

课程学习心得

总所周知,软件工程是实践性很强的专业,需要在实践中学习才能有深刻的理解。对于跨专业的我来说,一直希望获得与实际开发工作相似的环境,进而更深刻的理解在书本上学到的知识。就开源这一形式,听过不少对开源的溢美之词,却没能够理解其中的意义,直到跟着孟宁老师学习网络程序设计。在版本库阅览着各位大神优秀的代码以及详细的文档的过程中,慢慢懂得了开源的意义和关键所在,开源不仅仅是为了将智慧汇集在一起得到更优质的成果,还为后来者提供了一份学习的范例。

孟宁老师在讲授《网络程序设计》时给我们创造了一个真实的开发环境。跟以前学习其他课程做的实验不同,这门课让我们参与了一个软件项目的全部流程。项目初期,孟宁老师为了让大家尽快的熟悉机器学习基础理论和开发思想,采用了分享PPT的形式,让学习较快的同学带领着大家一起进步,这样的形式带动了大家的积极性,也包括我自己。机器学习理论和深度学习框架的分享贯穿了课程始终,每次都有同学分享自己学习的心得。与此同时,建立血常规模型进行预测的工作也如火如荼的进行,期间我也努力跟随着大家的进度,提交了几次pr,这个部分下面会有详细说明。

孟宁老师的授课方式很适合软件这个专业的特征,强调自主学习同时给予合适的引导,让整个学习过程有的放矢,能感受到压力却不会走弯路,使得整个学习成长曲线很平滑。只要跟着老师的要求尽力走下来,收获都不小。

在整个学习过程中,非常感谢孟宁老师和同学们的贡献,让整个项目从无到有,逐渐形成了一个代码规范、文档清晰的web系统。

个人贡献

个人贡献方面虽然有几次提交和合并,但都是基于老师以及课程中各位大神的带领下慢慢学习到的。特别是老师在项目各个阶段给出的具有针对性安排,班级的学习有条不紊的进行。某位同学在讨论时提出项目中要以数据集、算法、结构为工作重心,也是很不错的经验。

下面是相关贡献的截图,在caffe详细安装以及mnist例子pr过程中,由于没有即时更新版本库,导致不可合并。在caffe网络模型可视化的pr中,详细介绍了如何使用draw_net对caffe的prototxt网络模型进行可视化图片转换,生成的图片展示了创建的网络模型中各个层次以及输入输入。







draw_net.py

draw_net.py可以将网络模型由prototxt变成一张图片,draw_net.py存放在caffe根目录下python文件夹中。

绘制网络模型前,先安装两个库:GraphViz和pydot

draw_net.py执行的时候带三个参数

第一个参数:网络模型的prototxt文件

第二个参数:保存的图片路径及名字

第二个参数:–rankdir=x , x 有四种选项,分别是LR, RL, TB, BT 。用来表示网络的方向,分别是从左到右,从右到左,从上到小,从下到上。默认为LR。

python python/draw_net.py examples/mnist/lenet_train_test.prototxt ./lenet_train_test.jpg --rankdir=BT

#!/usr/bin/env python
"""
Draw a graph of the net architecture.
"""
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from google.protobuf import text_format
import caffe
import caffe.draw
from caffe.proto import caffe_pb2
def parse_args():
"""Parse input arguments
"""
parser = ArgumentParser(description=__doc__,
formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('input_net_proto_file',
help='Input network prototxt file')
parser.add_argument('output_image_file',
help='Output image file')
parser.add_argument('--rankdir',
help=('One of TB (top-bottom, i.e., vertical), '
'RL (right-left, i.e., horizontal), or another '
'valid dot option; see '
'http://www.graphviz.org/doc/info/'
'attrs.html#k:rankdir'),
default='LR')
parser.add_argument('--phase',
help=('Which network phase to draw: can be TRAIN, '
'TEST, or ALL.  If ALL, then all layers are drawn '
'regardless of phase.'),
default="ALL")
args = parser.parse_args()
return args
def main():
args = parse_args()
net = caffe_pb2.NetParameter()
text_format.Merge(open(args.input_net_proto_file).read(), net)
print('Drawing net to %s' % args.output_image_file)
phase=None;
if args.phase == "TRAIN":
phase = caffe.TRAIN
elif args.phase == "TEST":
phase = caffe.TEST
elif args.phase != "ALL":
raise ValueError("Unknown phase: " + args.phase)
caffe.draw.draw_net_to_file(net, args.output_image_file, args.rankdir,
phase)
if __name__ == '__main__':
main()


关于MNIST,这是一个非常有价值的手写字符集,官网为:http://yann.lecun.com/exdb/mnist/

据说该数据集中的图片是由美国中学生手写的数字,所以非常贴近实际,如果你的模型在该测试集上的测试精度能达到一定的程度,那么就可以说明你的模型的使用价值还是挺高的,说不定就可以用于手写邮政编码识别上。

项目介绍

对血常规检验报告的OCR识别、深度学习与分析

1将血常规检验报告的图片识别出年龄、性别及血常规检验的各项数据

1.1图片上传页面,提交的结果是图片存储到了mongodb数据库得到一个OID或到指定目录到一个path

1.2图片识别得到一个json数据存储到了mongodb数据库得到一个OID,json数据

1.2.1自动截取目标区域,已经能不同旋转角度的图片自动准备截取目标区域,但对倾斜透视的图片处理效果不佳,具体用法

1.2.2预处理,比如增加对比度、锐化

1.2.3识别

1.3识别结果页面,上部是原始图片,下部是一个显示识别数据的表格,以便对照识别结果

2学习血常规检验的各项数据及对应的年龄性别

3根据血常规检验的各项数据预测年龄和性别

运行环境

# 安装numpy,
sudo apt-get install python-numpy # http://www.numpy.org/ # 安装opencv
sudo apt-get install python-opencv # http://opencv.org/ 
##安装OCR和预处理相关依赖
sudo apt-get install tesseract-ocr
sudo pip install pytesseract
sudo apt-get install python-tk
sudo pip install pillow

# 安装Flask框架、mongo
sudo pip install Flask
sudo apt-get install mongodb # 如果找不到可以先sudo apt-get update
sudo service mongodb started
sudo pip install pymongo


运行

cd BloodTestReportOCR
python view.py # upload图像,在浏览器打开http://yourip:8080


view.py

Web 端上传图片到服务器,存入mongodb并获取oid,稍作修整,希望能往REST架构设计,目前还不完善; 前端采用了vue.js, mvvm模式。
imageFilter.py

对图像透视裁剪和OCR进行了简单的封装,以便于模块间的交互,规定适当的接口
imageFilter = ImageFilter() # 可以传入一个opencv格式打开的图片

num = 22
print imageFilter.ocr(num)


ocr函数 - 模块主函数返回识别数据

用于对img进行ocr识别,他会先进行剪切,之后进一步做ocr识别,返回一个json对象 如果剪切失败,则返回None @num 规定剪切项目数


perspect函数做 - 初步的矫正图片

用于透视image,他会缓存一个透视后的opencv numpy矩阵,并返回该矩阵 透视失败,则会返回None,并打印不是报告 @param 透视参数
关于param
参数的形式为[p1, p2, p3 ,p4 ,p5]。 p1,p2,p3,p4,p5都是整型,其中p1必须是奇数。
p1是高斯模糊的参数,p2和p3是canny边缘检测的高低阈值,p4和p5是和筛选有关的乘数。
如果化验报告单放在桌子上时,有的边缘会稍微翘起,产生比较明显的阴影,这种阴影有可能被识别出来,导致定位失败。 解决的方法是调整p2和p3,来将阴影线筛选掉。但是如果将p2和p3调的比较高,就会导致其他图里的黑线也被筛选掉了。 参数的选择是一个问题。 我在getinfo.default中设置的是一个较低的阈值,p2=70,p3=30,这个阈值不会屏蔽阴影线。 如果改为p2=70,p3=50则可以屏蔽,但是会导致其他图片识别困难。
就现在来看,得到较好结果的前提主要有三个
化验单尽量平整
图片中应该包含全部的三条黑线
图片尽量不要包含化验单的边缘,如果有的话,请尽量避开有阴影的边缘。


filter函数 - 过滤掉不合格的或非报告图片

返回img经过透视过后的PIL格式的Image对象,如果缓存中有PerspectivImg则直接使用,没有先进行透视 过滤失败则返回None @param filter参数
autocut函数 - 将图片中性别、年龄、日期和各项目名称数据分别剪切出来
用于剪切ImageFilter中的img成员,剪切之后临时图片保存在out_path, 如果剪切失败,返回-1,成功返回0 @num 剪切项目数 @param 剪切参数
剪切出来的图片在BloodTestReportOCR/temp_pics/ 文件夹下
函数输出为data0.jpg,data1.jpg......等一系列图片,分别是白细胞计数,中性粒细胞记数等的数值的图片。
classifier.py
用于判定裁剪矫正后的报告和裁剪出检测项目的编号
imgproc.py
将识别的图像进行处理二值化等操作,提高识别率 包括对中文和数字的处理
digits
将该文件替换Tesseract-OCR\tessdata\configs中的digits


项目运行截图

1、在浏览器打开http://yourip:8080



2、上传需要预测的血常规报告,如果上传的报告不合法,系统不会接受。



3、识别出报告单中各个项目的数据,一共二十二个项目。



4、预测得出年龄和性别的结果,目前预测不够准确,还有待改进。



项目模块分析

view.py
Web 端上传图片到服务器,存入mongodb并获取oid,稍作修整,希望能往REST架构设计,目前还不完善; 前端采用了vue.js, mvvm模式。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
from cStringIO import StringIO
import bson
import cv2
import flask
import numpy
from PIL import Image
from bson.json_util import dumps
from flask import Flask, request, Response, jsonify, redirect, json
from pymongo import MongoClient
from werkzeug.utils import secure_filename
import tf_predict
from imageFilter import ImageFilter
app = Flask(__name__, static_url_path="")
# 读取配置文件
app.config.from_object('config')
# 连接数据库,并获取数据库对象
db = MongoClient(app.config['DB_HOST'], app.config['DB_PORT']).test
# 将矫正后图片与图片识别结果(JSON)存入数据库
def save_file(file_str, f, report_data):
content = StringIO(file_str)
try:
mime = Image.open(content).format.lower()
print 'content of mime is:', mime
if mime not in app.config['ALLOWED_EXTENSIONS']:
raise IOError()
except IOError:
abort(400)
c = dict(report_data=report_data, content=bson.binary.Binary(content.getvalue()), filename=secure_filename(f.name),
mime=mime)
db.files.save(c)
return c['_id'], c['filename']
@app.route('/', methods=['GET', 'POST'])
def index():
return redirect('/index.html')
@app.route('/upload', methods=['POST'])
def upload():
if request.method == 'POST':
if 'imagefile' not in request.files:
flash('No file part')
return jsonify({"error": "No file part"})
imgfile = request.files['imagefile']
if imgfile.filename == '':
flash('No selected file')
return jsonify({"error": "No selected file"})
if imgfile:
# pil = StringIO(imgfile)
# pil = Image.open(pil)
# print 'imgfile:', imgfile
img = cv2.imdecode(numpy.fromstring(imgfile.read(), numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
report_data = ImageFilter(image=img).ocr(22)
if report_data == None:
data = {
"error": 1,
}
return jsonify(data)
with open('temp_pics/region.jpg') as f:
if f is None:
print 'Error! f is None!'
else:
'''
定义file_str存储矫正后的图片文件f的内容(str格式),方便之后对图片做二次透视以及将图片内容存储至数据库中
'''
file_str = f.read()
'''
使用矫正后的图片,将矫正后图片与识别结果(JSON数据)一并存入mongoDB,
这样前台点击生成报告时将直接从数据库中取出JSON数据,而不需要再进行图像透视,缩短生成报告的响应时间
'''
#img_region = cv2.imdecode(numpy.fromstring(file_str, numpy.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
#report_data = ImageFilter(image=img).ocr(22)
fid, filename = save_file(file_str, f, report_data)
print 'fid:', fid
if fid is not None:
templates = "<div><img id=\'filtered-report\' src=\'/file/%s\' class=\'file-preview-image\' width=\'100%%\' height=\'512\'></div>" % (
fid)
data = {
"templates": templates,
}
return jsonify(data)
# return render_template("result.html", filename=filename, fileid=fid)
# return render_template("error.html", errormessage="No POST methods")
return jsonify({"error": "No POST methods"})
'''
根据图像oid,在mongodb中查询,并返回Binary对象
'''
@app.route('/file/<fid>')
def find_file(fid):
try:
file = db.files.find_one(bson.objectid.ObjectId(fid))
if file is None:
raise bson.errors.InvalidId()
return Response(file['content'], mimetype='image/' + file['mime'])
except bson.errors.InvalidId:
flask.abort(404)
'''
直接从数据库中取出之前识别好的JSON数据,并且用bson.json_util.dumps将其从BSON转换为JSON格式的str类型
'''
@app.route('/report/<fid>')
def get_report(fid):
# print 'get_report(fid):', fid
try:
file = db.files.find_one(bson.objectid.ObjectId(fid))
if file is None:
raise bson.errors.InvalidId()
print 'type before transform:\n', type(file['report_data'])
report_data = bson.json_util.dumps(file['report_data'])
print 'type after transform:\n', type(report_data)
if report_data is None:
print 'report_data is NONE! Error!!!!'
return jsonify({"error": "can't ocr'"})
return jsonify(report_data)
except bson.errors.InvalidId:
flask.abort(404)
def update_report(fid,ss):
# load json example
with open('bloodtestdata.json') as json_file:
data = json.load(json_file)
for i in range(22):
data['bloodtest'][i]['value'] = ss[i]
json_data = json.dumps(data, ensure_ascii=False, indent=4)
db.files.update_one({
'_id': bson.objectid.ObjectId(fid)}, {
'$set': {
'report_data': json_data
}
}, upsert=False)
file = db.files.find_one(bson.objectid.ObjectId(fid))
report_data = bson.json_util.dumps(file['report_data'])
print report_data

@app.route('/predict/<fid>', methods=['POST'])
def predict(fid):
print ("predict now!")
data = json.loads(request.form.get('data'))
ss = data['value']
# 若用户在输入框中对数值进行修正,则更新mongodb中的数据
update_report(fid,ss)
arr = numpy.array(ss)
arr = numpy.reshape(arr, [1, 22])
sex, age = tf_predict.predict(arr)
result = {
"sex":sex,
"age":int(age)
}
return json.dumps(result)
if __name__ == '__main__':
app.run(host=app.config['SERVER_HOST'], port=app.config['SERVER_PORT'])

classifier.py

将pHash算法集成到现有代码中判断图片是否为血常规报告
# -*- coding: UTF-8 -*-
import pHash
from PIL import Image
import os
# 判断是否血常规检验报告,输入经过矫正后的报告图像
def isReport(img):
# add your code here
image = Image.open(os.getcwd() + '/origin_pics/region.jpg')
rate=pHash.classify_DCT(image,img)/64.0
if(rate>0.6):
return True
else:
return False
# 根据剪裁好的项目名称图片获得该项目的分类号,注意不是检验报告上的编号,是我们存储的编号
num = 0
def getItemNum(img):
# replace your code
global num
if num >= 22:
num = 0
ret = num
num = num + 1
return ret
# unit test
if __name__ == '__main__':
import classifier
img = []
if classifier.isReport(img) :
print 'classifier.isReport(img) is True'
for i in range(33):
print classifier.getItemNum(img)

项目总结

经过这一个学期的学习,在深度学习和软件文档方面收获颇丰,孟宁老师是一个非常优秀并且有创意的老师,总能引导我们以最贴合实际应用的方式学习,希望以后还能选到孟宁老师的课程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息