相关源码和文档获取请私聊QQ:3106089953
目 录
摘 要 I
Abstract II
目 录 IV
第1章 绪论 1
1.1. 研究背景与意义 1
1.2. 国内外研究现状 2
1.2.1. 文本定位技术研究现状 2
1.2.2. 手写汉字识别研究现状 3
1.2.3. 汉字书写质量评价方法研究现状 4
1.3. 本文所做工作 4
1.4. 论文结构安排 5
第2章 系统需求分析及概要设计 6
2.1. 可行性分析 6
2.2. 系统用例分析 7
2.2.1. 用例表设计 7
2.3. 功能需求分析 9
2.4. 性能需求分析 10
2.5. 系统总体设计 10
2.5.1. 系统总体架构设计 10
2.5.2. 系统功能模块设计 12
2.6. 相关技术介绍 13
2.6.1. Android系统 13
2.6.2. OpenCV视觉库 14
2.6.3. TensorFlow开源库 14
2.7. 本章小结 14
第3章 系统详细设计及实现 16
3.1. 系统模块详细设计 16
3.2. 笔画提取模块 16
3.2.1. 单帧图像的疑似笔画像素点检测 17
3.2.2. 基于帧差法的笔画增长点检测 19
3.2.3. 笔画切换点检测与单笔画的提取 20
3.3. 基本笔画识别模块 22
3.3.1. 汉字的基本笔画 22
3.3.2. LeNet-5网络 23
3.4. 书写评分模块 25
3.4.1. 重心特征 27
3.4.2. 网格特征 27
3.4.3. 整字综合评分 27
3.5. 系统UI界面 28
3.6. 本章小结 30
第4章 系统性能测试 31
4.1. 测试的意义与方法 31
4.2. 运行性能测试 32
4.2.1. 测试设备 32
4.2.2. 测试环境 32
4.3. 本章小结 33
总结 34
论文工作总结 34
工作展望 34
参考文献 35
致谢 37
2.3.功能需求分析
系统的用例分析是从用户操作的角度对系统进行设计,对于系统的功能需求分析,是以开发者的角度进行设计,即对系统的用例图及用例描述进行详细定义与描述,给开发人员作为参考。所有功能均与系统用例一一对应。
1)录制书写过程
该功能通过用户点击按钮触发,软件获取系统摄像头使用权限,开启摄像头,进行视频录制,直至用户点击按钮结束录制。输出mp4多媒体格式文件并传递给下一模块。
2)报告书写正确性
该功能触发前,需要用户输入书写的汉字,以供系统使用相应的模板特征数据与输入数据进行对比分析。系统对录制好的书写过程视频进行计算,提取出单一笔画,通过笔画识别、特征比对等方式得到书写正确性结果。书写正确性包括笔画类型、笔画位置、笔画顺序等方面的正确性。报告结果在文本框中以文本方式显示。后台给出笔画状态编码列表,前端对其映射得到对应文本。
3)显示书写改正建议
该功能依赖于报告书写正确性功能的分析结果。改正建议类型与正确性类型相对应,因此同样使用映射得到对应的建议文本,在前端的文本框中显示。
4)显示书写评分
该功能与报告书写正确性、显示书写改正建议一同显示。后台对单一笔画、整字分别通过特征比对法计算评分,最后将其权值相加以百分制形式给出用户的综合书写评分。
2.4.性能需求分析
系统的功能需求仅仅可以确定系统的任务,但是为了让系统的能够更长久且无误的运行,还需要进行系统的性能需求分析。系统的性能需求是按照系统的功能需求以及系统本身的特性所设定的。将从系统的开发、系统的运行以及部署等多个方面,进行性能的提取。当系统的性能满足需求后,系统的生命周期才会满足预估,且也会使得系统的功能更加完备。
1.可靠性
系统需要具备一定的可靠性,即用户完成视频输入后,在设备运行内存充足的情况下,系统应该在5秒内完成计算,给出反馈结果。
2.容错性
用户在进行功能操作时,若输入了错误的数据,如用户在汉字书写过程中,出现连笔、错字的情况等,用户在输入信息时输入了错误的形式信息后,系统不会自动闪退或崩溃,而是将这些错误情况进行捕捉,并显示哪里出错,让用户进行及时更改。
3.可维护性
系统需要具备一定的可维护性,随着后续的算法优化与替换,书写评价功能的增加等,所有的模块均应该是可维护的,且可增加、替换或者删除的,系统在设计过程中需要达到高内聚低耦合的状态。
2.5.系统总体设计
2.5.1.系统总体架构设计
系统总体架构设计是指按照多种角度进行分层,本系统的总体架构如图2-2所示。系统可分为访问层、前端UI、业务层、数据层及基础设施层[15]。
对于访问层,有Android移动端及安卓用户作为终端。系统的所有功能均是通过这一硬件进行数据计算与显示的。
前端UI对应系统的页面展示与用户交互,该系统部署在App端,使用Android操作系统,以PyQt5作为GUI开发框架,本文转载自http://www.biyezuopin.vip/onews.asp?id=14866需要完成对Activity的编写以及Video的读写。
系统的业务层是将系统的功能以业务逻辑的形式展示在系统的总体架构图中。对于用户,主要包含录取视频、选择汉字等业务逻辑功能。对于后台的业务逻辑主要包含视频录制模块、书写评价模块,其中视频录制模块包括录取视频,书写评价模块包括笔画提取、笔顺判别、文本定位与切割、书写评分等功能。
数据层主要包含了数据的存储过程、数据缓存、文件读写等数据操作。
基础设施层对应了系统的物理架构所需要的硬件设备,包括操作系统、摄像头、CPU等。
import cv2
import sys
import numpy as np
from matplotlib import pyplot as plt
import pickle
from text_location import *
char_feat_path = './utils/character_feature_dict'
stroke_feat_path = './utils/stroke_feature_dict'
def gravity_core(img): # 计算汉字的重心特征向量
'''
计算汉字的重心特征向量
----------
:param img [ndarray]: 笔画二值图像,笔画为白色
:return [list]: 归一化的重心坐标
:return [list]: 绝对重心坐标
'''
h, w = img.shape
x_axis = 0
y_axis = 0
total = np.sum(img[:, :])
for row in range(h):
y_axis += row * np.sum(img[row, :])
Gy = y_axis / total
for col in range(w):
x_axis += col * np.sum(img[:, col])
Gx = x_axis / total
#除是进行了归一化处理,消除了输入图像分辨率的差异对评分的影响
return [Gx/w, Gy/h], [Gx, Gy]
def grid_vector(img, n_divide = 3): # 计算图像的网格特征向量
'''
计算汉字的网格特征向量
----------
:param img [ndarray]: 笔画二值图像,笔画为白色
:param n_divide [int]:
:return [list]:
'''
w, h = img.shape
spanX = int(w/n_divide)
spanY = int(h/n_divide)
grid_area = spanX * spanY
left_border = 0
up_border = 0
grid_vector = []
#求出每一个矩阵的特征值
for row in range(n_divide):
left_border = 0
for col in range(n_divide):
white_num = np.sum(img[up_border:up_border+spanY, left_border:left_border+spanX])
#在这个矩形之中 一共有多少个方格是有白色像素的,然后除以这个矩形的面积
grid_vector.append(white_num / grid_area)
left_border += spanX
up_border += spanY
return grid_vector
def cosine_similarity(vec1, vec2):
'''
计算向量余弦相似度
----------
:param vec1 [list]: 向量1,元素应为可计算数值
:param vec2 [list]: 向量2,元素应为可计算数值
:return [float]: 相似性数值,取值范围为[-1,1]
'''
#求余弦相似度的公式
sim = np.dot(vec1, vec2) / (np.linalg.norm(vec1)*np.linalg.norm(vec2))
return sim
def stroke_score(stroke_list, char_id, dict_path, gravity_ratio=0.2):
'''
根据单笔画分别评分
----------
:param img [ndarray]: 笔画二值图像列表,笔画为白色
:param char_id [str]: 汉字编号
:param dict_path [str]: 模板特征字典文件路径
:param gravity_ratio [float]: 重心特征评分所占比例,为0到1之间的值
:return [float]: 笔画评分
'''
if gravity_ratio>1.0 or gravity_ratio<0:
print("单笔画重心评分比例错误,应为0到1之间的值")
gravity_ratio = 0.2
grid_ratio = 1.0 - gravity_ratio
pattern_feat = []
score = []
with open(dict_path, 'rb') as f:
dict = pickle.load(f)
pattern_feat = dict[char_id]
if len(pattern_feat):
if len(pattern_feat)==len(stroke_list):
for i in range(len(pattern_feat)): # 对每个笔画分别计算特征向量,计算评分
gravity, _ = gravity_core(stroke_list[i]) #重心特征
grid = grid_vector(stroke_list[i]) #网络特征
gravity_similarity = cosine_similarity(gravity, pattern_feat[i][0])
grid_similarity = cosine_similarity(grid, pattern_feat[i][1])
# test
print("单笔画重心评分", gravity_similarity)
print("单笔画网格评分", grid_similarity)
print()
# 这是笔画的评分
score.append(gravity_similarity * gravity_ratio + grid_similarity * grid_ratio)
return np.mean(score)
else:
print("【ERROR】 笔画图片数量与模板不一致!")
sys.exit(0)
return -1
else:
print("模板数据丢失!")
return -1
#上面是检测单个笔画的,这个是整个字的,用的方法是一模一样
def char_score(char_img, char_id, dict_path, gravity_ratio=0.2):
'''
对整字进行评分
----------
:param img [ndarray]: 汉字二值图像,文字部分为白色
:param char_id [str]: 汉字编号
:param dict_path [str]: 模板特征字典文件路径
:param gravity_ratio [float]: 重心特征评分所占比例,为0到1之间的值
:return [float]: 整字评分
'''
if gravity_ratio>1.0 or gravity_ratio<0:
print("单笔画重心评分比例错误,应为0到1之间的值")
gravity_ratio = 0.2
grid_ratio = 1.0 - gravity_ratio
with open(dict_path, 'rb') as f:
dict = pickle.load(f)
pattern_feat = dict[char_id]
if len(pattern_feat):
gravity, _ = gravity_core(char_img)
grid = grid_vector(char_img)
gravity_similarity = cosine_similarity(gravity, pattern_feat[0])
grid_similarity = cosine_similarity(grid, pattern_feat[1])
# test
print("整字重心评分", gravity_similarity)
print("整字网格评分", grid_similarity)
print()
return (gravity_similarity * gravity_ratio + grid_similarity * grid_ratio)
else:
print("模板数据丢失!")
return -1
def aesthetic_score(stroke_list, char_id):
'''
计算汉字的书写质量综合评分
----------
:param stroke_list [list]: 笔画二值图像列表,笔画为白色
:return [float]: 书写评分
'''
# 计算单笔画评分
strokes_score = stroke_score(stroke_list, char_id, stroke_feat_path)
# 计算整字评分
char_img = img_merge(stroke_list)
character_score = char_score(char_img, char_id, char_feat_path)
# 权值相加得到综合评分
stroke_ratio = 0.5
char_ratio = 1.0 - stroke_ratio
final_score = strokes_score * stroke_ratio + character_score * char_ratio
return final_score
if __name__ == '__main__':
stroke_list = []
for i in range(4):
img = cv2.imread("./test_video/strokes/" + str(i) + ".jpg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
stroke_list.append(img)
stroke_list = text_location(stroke_list) # 文本定位与切割
score = aesthetic_score(stroke_list, '0750')
print(score)