由于往年计算综测分是件繁琐而又复杂的事情,所以写了个综测分计算工具来帮助解决这种问题,后来逐渐诞生出三个版本,一个是本地版本
local_version,一个是web版本web_version,还有一个是数据库版本web_db_version,使用的技术主要为flask快速搭建后台+baiduAI文字识别,以及layui做响应式布局书写于2019/08/08 18:17
OCR识别成绩和科目
这里识别的是奕报告的成绩截图,将里面的成绩和科目提取出来,这样就可以快速计算基础分了
-
这里识别的是奕报告的成绩截图,利用文字基础识别的
分块识别的特点,将里面的成绩和科目提取出来,这样就可以快速计算基础分了
-
这里由于做的是一个小工具,使用的是
python语言,所以用的是里面提供的python的sdk,比较方便一点,当然你也可以通过提供里面提供的API来进行识别,不过就是没有sdk方便就是了百度智能云-Python文字识别SDK文档 -
所以首先安装
OCR Python SDK1
pip install baidu-aip -
快速创建一个通用识别文字的应用,这里还有很多参数和方法可以使用,具体看文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from aip import AipOcr """ 你的 APPID AK SK """ APP_ID = '你的 App ID' API_KEY = '你的 Api Key' SECRET_KEY = '你的 Secret Key' """ 读取图片 """ def get_file_content(filePath): with open(filePath, 'rb') as fp: return fp.read() image = get_file_content('example.jpg') client = AipOcr(APP_ID, API_KEY, SECRET_KEY) """ 带参数调用通用文字识别 """ client.basicGeneral(image)注意这里的
APP_ID和APP_KEY以及SECRET_KEY需要自己去百度智能云的官网创建一个应用后获取百度智能云-文字识别
-
当你使用上面的代码来识别奕报告的截图的时候,会发现识别的结果具体如下

发现是个字典类型,并且每个文字块都被分开了,成绩就在
综合成绩:96这块里面,而科目信息就在成绩的前面,所以需要做的事情也就是使用正则表达式查找包含综合成绩的信息,并将成绩提取出来,而后取前一个信息作为科目1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20""" 获取成绩信息 """ def get_grade_info(wordsResult): grades = {} for i in range(len(wordsResult)): # 获取wordsResult里面的"综合成绩:100"的结果 words = wordsResult[i]['words'] # 正则表达式匹配成绩 matchObj = re.match(r'综合成绩:(.*)',words) # 如果words里面存在"综合成绩:100"的情况 if(matchObj): grade = matchObj.group(1) # 科目是成绩前一个的信息 subject = wordsResult[i-1]['words'] grades[subject] = grade return grades # 通用文字识别 wordsResult = client.basicGeneral(image)['words_result'] # 获取成绩字典 grades = get_grade_info(wordsResult) -
但是实际上在提取文字信息的时候会发现存在一些问题,比如出现成绩和科目混在一块的问题,具体还需要做些算法进行补偿

-
通过优化这样就可以写成一个本地的版本zongce/local_version,这里将图片放在image目录下,然后调用
ocr.py就可以实现批量识别了
Web端的开发
Flask快速搭建后台
这里使用Python的后台框架flask来搭建后台,方便快捷
-
首先安装flask
1
pip install flask -
这里先贴出flask的后台代码,是不是难以相信这么少的代码就可以做一个后台
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# -*- coding:UTF-8 -*- from flask import render_template,Flask,request from getGrades import get_grades import os import json import random app = Flask(__name__) # 根路由 @app.route('/', methods=['GET', 'POST']) def index(): # 根路由返回static下的index.html目录 return app.send_static_file('index.html') # 通过getgrades路由实现图片的上传和结果的返回 @app.route('/getgrades', methods=['POST']) def getGrades(): if 'file' in request.files: # 获取图片 file = request.files['file'] # 设置图片名 fileName = str(random.randint(1000,9999))+file.filename # 保存图片 file.save(fileName) # 识别成绩和科目 grades = get_grades(fileName) # 删除图片 os.remove(fileName) # 返回json数据 return json.dumps(grades,ensure_ascii=False) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)其中已经将上一步的成绩识别写成了一个包
getGrades,方便调用,这里仅返回科目和成绩,返回前端的窗口显示,这里有两个路由,根路由负责前端页面,发送我们事先写好的index.html页面,另一个是负责图片的上传和返回成绩的识别结果 -
之后就是前端页面
index.html的书写,由于有个大神做了一个简单的js版本的本地html页面,这里直接修改代码实现图片的上传和成绩结果的渲染,这里使用jQuery来实现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<!--成绩的输入和渲染的地方--> <div> 无线传感器网络技术 </div> <input type="number" id="无线传感器网络技术"> <!--文件上传的地方--> <input type="file" id="image"/> <input type="submit" id="upload"/> <script type="text/javascript"> // 成绩渲染函数,遍历json数据 function setGrade(json) { for (var key in json) { $('#' + key).val(json[key]); } } // 点击上传按钮 $('#upload').click(function (){ // 获取file里面的图片文件 var file = document.getElementById("image").files[0]; // js获取文件对象 var data = new FormData(); data.append("file", file); //调用接口 $.ajax({ url: "/getgrades", data: data, type: "Post", dataType: "json", cache: false,//上传文件无需缓存 processData: false,//用于对data参数进行序列化处理 这里必须false contentType: false, //必须 success: function (result) { //渲染成绩 setGrade(result); }, }) }); </script> -
当然基础分的计算和具体优化的操作可以通过js来实现,这里就不贴出来了,由于在写代码的过程中没使用git,所以初代的版本没有图片贴出,接下来是直接进行美化
layui美化项目
layui是一个前端的响应式框架,界面挺好看的所以使用了
-
这里分别使用了它的
表单和导航,弹出层以及文件上传,如果你使用的仅仅是它的样式的话,那么直接使用导入它包里面的layui.css就可以了1
<link rel="stylesheet" href="layui.css" media="all">其他的从它的实例代码里面搬运就行了,这里只用到样式的是表单 - 在线演示 - layui和导航菜单/面包屑 - 在线演示 - layui
-
但是当我再用到像layer弹出层 - 在线演示 - layui和文件/图片上传 - 在线演示 - layui需要使用到js的样式时,就要将整个layui整个包下载下来放在项目里,也就600多kb而已

上面的图片是layui包的路径,在调用的时候要调用css和js
1
2<link rel="stylesheet" href="static/layui/css/layui.css" media="all"> <script src="static/layui/layui.js" charset="utf-8"></script> -
接下来就是实现弹出层和文件上传的功能了,需要改写之前的代码
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
30layui.use('layer', function () { var $ = layui.jquery, layer = layui.layer; // 刚打开页面弹出信息 layer.open({ offset: 'auto', title: '注意', content: "如果使用奕报告截图识别,请注意核对成绩" }) }); // 点击上传图片的时候自动上传文件 layui.use('upload', function () { var upload = layui.upload; //执行实例 var uploadInst = upload.render({ elem: '#upload' //绑定元素 , url: '/getgrades' //上传接口 , done: function (res) { if (JSON.stringify(res) == "{}") { layer.msg('请上传正确的奕报告截图'); } else { setGrade(res); layer.msg('识别成功,注意核对成绩'); } } , error: function () { layer.msg('上传出现错误'); } }); }); -
到这里就可以展示一下改造后的项目了web_version iAoe444/zongce



项目的部署
❗待完成
动态页面的开发
由于这里的绩点和科目都是写死的,不方便后期的修改,那么写一个连接数据库的动态页面就很方便了,这里使用mysql做数据库,使用pymyql进行数据库操作
-
数据库建表
1
2
3
4
5
6
7
8
9DROP TABLE IF EXISTS `tb_credit`; CREATE TABLE `tb_credit` ( `id` int(11) NOT NULL AUTO_INCREMENT, `subject` varchar(255) DEFAULT NULL COMMENT '科目', `credit` float(3,1) DEFAULT '4.0' COMMENT '学分', `term` int(2) DEFAULT '1' COMMENT '学期(1代表第一学期,2代表第二学期)', `enable_status` int(2) DEFAULT '1' COMMENT '1代表显示,0代表不显示', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; -
安装
pymysql包1
pip install pymysql -
快速入门
pymysql1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import pymysql db = pymysql.connect("localhost","root","123456","zongce") cursor = db.cursor() """获取科目和对应的学分""" def getCredits(): # 执行的sql语句 sql = "SELECT * FROM tb_credit where enable_status=1" # 使用 execute() 方法执行 SQL 查询 cursor.execute(sql) # 使用 fetchall() 方法获取所有数据. results = cursor.fetchall() # 获取科目,学分和学期 for row in results: subject = row[1] credit = row[2] term = row[3] db.close() -
制作一个类实现类似spring里的service层功能
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
38import pymysql class DAO: # 初始化 def __init__(self): # 创建连接 self.conn = pymysql.Connect( host='localhost', port=3306, user='root', password='123456', db='zongce', charset='utf8', cursorclass=pymysql.cursors.DictCursor # 以字典的形式返回数据 ) # 获取游标 self.cursor = self.conn.cursor() """获取科目和对应的学分""" def getCredits(self): credits = [] sql = "SELECT subject,credit,term FROM tb_credit where enable_status=1" self.cursor.execute(sql) for row in self.cursor.fetchall(): credits.append(row) return credits """补偿科目信息""" def completeSubject(self, need_completed_subject): sql = "SELECT subject FROM tb_credit where subject like '%" + \ need_completed_subject + "%'" self.cursor.execute(sql) return self.cursor.fetchone() # 关闭连接 def close(self): self.cursor.close() self.conn.close() -
之后调用这个就行了,接着是将数据渲染到html页面上
1
2
3
4
5
6
7
8@app.route('/', methods=['GET', 'POST']) def index(): # 实例化类 dao = DAO() # 调用方法 credits = dao.getCredits() # 传递credits到模板上 return render_template('index.html',credits=credits) -
接着是模板html的部分代码,注意要放在
templates文件夹下1
2
3
4
5
6<div class="layui-form"> <fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;"> <legend>第一学期成绩</legend> </fieldset> </div> -
接下来就是其他优化了,至此三个版本就介绍完毕了,完成这样一个小项目也算是对之前的学习的内容的总结,也算是小有成就感成就感