部分内容参考
这个代码是我大四的时侯东拼西凑下来的,应付结业设计,里面好多代码写的都很low,且效率低下,现在忙着工作也懒得改了,有兴趣者自己更改或则构建吧 < G _ O > 还有数据集的问题,之前备份的硬碟坏掉了,我也没了!!!有想法者可以去找一找,我当时也是找了许久才见到有其他博主分享的
目录
1.摘要
项目主要针对基于视频的计算机唇读系统中唇部检查、唇读特点提取和唇语辨识等关键技术进行了研究。具体来说,首先对数据进行预处理,包括对视频进行切帧和增广处理;然后采用Yolov5算法对唇部检查并截取有效区域进行后续处理;接着设计了一个新型网路用于唇部特点的提取和辨识。该网路融合了3DResNet和GRU网路,能够同时借助视频数据的空间和时间信息,进而提取高效的特点获得较好的辨识结果。最后,为了彰显可视化和实用性,本文使用Flask框架实现了唇读系统的各个功能,可以在web端体验唇读系统的辨识疗效。
2.项目流程
研究的主要内容是对英文唇语成语的辨识功能,研究的问题涉及到唇部定位、数据特点提取、网络对特点的预测结果以及web端实现前前端数据交互四个方面。而最为关键的,就在于特点提取以及预测结果两个方面,重点设计可行的深度学习方案,对比各类方式来研究出本文的最佳实现手段。对于模型的鲁棒性和泛化能力,项目须要大量而又无误的数据集支持,以及采用合适该数据集的网路模型,才能保证网路拥有强有力的表现能力。
在系统功能上,用户须要登陆成功能够使用唇语辨识功能,项目流程如图所示。
在技术路线上,第一个模块是目标测量,该模块须要确切的找到人脸的唇部位置,并且通过预测的座标对图象进行切割,保证唇部坐落图象的最中间位置,项目实现算法为Yolov5算法,采用最小的预训练模型进行训练。在目标测量数据集的制做中,需要保证数据的完整性,并且标明的眼睛应当坐落图象的最中间位置声音识别算法源代码,达到唇部定位的疗效,这样可以大程度推动后续分类网路的拟合速率。
第二模块采用的是3DResNet与GRU复合式网路,通过Yolov5算法处理过的数据传入该网路中,残差结构提取特点,GRU保证时序信息的传递与保存,再通过softmax得到预测的结果。在该模块中,训练数据的数目尤为关键,所以在预处理中,使用数据增广让网路有充足的数据训练。其次,网络的结构也十分重要,残差网路ResNet是由多层网路堆叠而成,解决网路深度引起的梯度消失问题,让网路更深,提取到的图象信息特点越多越有效。而循环神经网路RNN的变种体GRU则是通过了门的控制机制,使时序信息得到挺好的保留,让神经网路愈发关注的是时间序列的唇部动态变化信息。
预测结果最终传入web模块,依靠html和js完成前前端的交互,实现的系统的辨识功能。在web模块中,所使用的框架是Flask框架,该框架优点就是轻巧灵活,在登陆功能中html直接往前端递交表单,后端只要通过数据库对表单进行校准,就可以完成登陆功能。在辨识功能中,通过js配合html读取本地视频,再递交到后台进行处理,处理过程首先是经过视频切帧,然后Yolov5模型对帧率图象进行唇部座标预测,再切割图象并保存,最后通过分类网路得到预测的结果,识别的词句展示到后端页面即可完成整个功能的流程,处理的流程如图所示。
项目采用的技术为现今最主流的one-stage目标测量算法Yolo,用来辅助方差网路ResNet对视频进行唇语翻译,该目标测量算法首次应用于唇语辨识中,使系统达到端到端的辨识疗效。其次数据集是由多帧率图象组成单一样本,存在缺帧等问题,给项目带来了一定的难度。项目还采用了python轻量级web框架Flask,通过后端html和js的配合使用来操作深度学习算法,达到视觉上的展示疗效,让用户可以使用网页端操作唇语辨识系统。
3.部分代码展示 3.1.代码结构
项目由三部份构成,唇读模块、目标测量模块和web端demo模块,由于目标测量的模型路径问题,将demo和yolov5整合到了一个目录下。
3.2.demo主程序
post恳求将后端读取的视频先保存至本地相应目录,再做后续相关操作。
@app.route('/predict', methods=['GET', 'POST'])
def predict():
if request.method == 'POST':
try:
# 读取video文件
f = request.files['file']
# 保存前端读取的视频到uploads
basepath = args.save_video
file_path = os.path.join(basepath, secure_filename(f.filename))
f.save(file_path)
img_list = video_to_frames(file_path)
cut_img_list = cut_img(img_list)
del img_list
vocab_path = 'lip_models/vocab100.txt'
# 载入网络进行预测
result = model_predict(model, cut_img_list, vocab_path, args.device)
result = str(result[0])
print("识别结果:" + result)
return result
except Exception as e:
return "错误,无法正确识别"
return None
3.3.视频切帧
切帧直接存入list返回
def video_to_frames(path):
"""
输入:path(视频文件的路径)
"""
# VideoCapture视频读取类
# 抽取帧数
videoCapture = cv2.VideoCapture()
videoCapture.open(path)
# 总帧数
frames = videoCapture.get(cv2.CAP_PROP_FRAME_COUNT)
img_list = []
for i in range(int(frames)):
ret, frame = videoCapture.read()
if i % 4 == 0:
img_list.append(frame)
print("视频切帧完成!")
return img_list
3.4.目标测量模块处理
重新建立一个类,将加载模型初始化,由于只有一个类别,因此classes为0,定义detect方式,传入参数为图象的list,返回结果为对应座标的list。
class yolov5(object):
def __init__(self,
img_size = 416,
weights = 'runs/train/exp/weights/best.pt',
iou_thres = 0.45,
conf_thres = 0.25,
device = '0',
classes = 0,
agnostic_nms = False,
augment = False
):
self.imgsz = img_size
self.iou_thres = iou_thres
self.conf_thres = conf_thres
self.device = select_device(device)
self.classes = classes
self.agnostic_nms = agnostic_nms
self.augment = augment
# Initialize
set_logging()
self.half = self.device.type != 'cpu' # half precision only supported on CUDA
# Load model
self.model = attempt_load(weights, map_location=self.device) # load FP32 model
def detect(self, source):
stride = int(self.model.stride.max()) # model stride
imgsz = check_img_size(self.imgsz, s=stride) # 检查图片的大小
if self.half:
self.model.half() # to FP16
cudnn.benchmark = True # 设置True可以加速恒定图像大小的处理速度
# Run inference
if self.device.type != 'cpu':
self.model(torch.zeros(1, 3, imgsz, imgsz).to(self.device).type_as(next(self.model.parameters()))) # run once
result = []
for img0 in source:
imgsz = check_img_size(imgsz) # check img_size
img = letterbox(img0, imgsz, stride=32)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(self.device)
img = img.half() if self.half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# 获取模型预测
pred = self.model(img, augment=self.augment)[0]
# 使用NMS进行预测
pred = non_max_suppression(pred, self.conf_thres, self.iou_thres, classes=self.classes,
agnostic=self.agnostic_nms)
# 过程检测
for i, det in enumerate(pred): # 遍历预测框
# 还原图像坐标值大小
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], img0.shape).round()
result.append(det[0][:4].tolist())
return result
3.5.图片剪裁
目标测量模块得到唇部座标,然后剪裁保存至新的list中返回。
# 根据预测得到的坐标进行裁剪
def cut_img(img_list):
# 进行目标检测得到坐标点
result = yolov5_det.detect(img_list)
cut_img_list = []
for idx, image in enumerate(img_list):
labels = result[idx]
cropped = image[int(labels[1]): int(labels[3]), int(labels[0]):int(labels[2])]
cut_img_list.append(cropped)
print("嘴型检测并裁剪完成!")
return cut_img_list
3.6.唇读推理模块预处理
def _sample(cut_img_list, bilater = True):
data = []
for img in cut_img_list:
img = img_clip(img) # 缩放并填充至112大小
if bilater and random.random() < 0.6:
# 引入双边滤波去噪
img = cv2.bilateralFilter(src=img, d=0, sigmaColor=random.randint(15, 30), sigmaSpace=15)
# 归一化,转换数据类型 并限定上下界限的大小必须为fixed_side
img = img.astype(np.float32)
# 标准化处理
img -= np.mean(img) # 减去均值
img /= np.std(img) # 除以标准差
data.append(img)
return np.array(data)
3.7.模型预测
网络输出后获取最大值的下标,结合词表得到预测的唇读词句类别
def model_predict(model, cut_img_list, vocab_path, device):
model.to(device)
id2label = []
with open(vocab_path, 'r', encoding='utf-8') as f:
for word in f:
id2label.append(word.split(',')[0])
# 预处理
test_data = process(cut_img_list)
test_data = torch.tensor(padding_batch(test_data))
print("数据预处理完成!")
##############################
# 预测
##############################
pre_result = []
with torch.no_grad():
batch_inputs = test_data.to(device)
logist = model(batch_inputs)
pred = torch.argmax(logist, dim=-1).tolist()
pre_result.append(id2label[pred[0]])
return pre_result
4.核心技术剖析 4.1.数据剖析
在数据方面,项目使用的数据集是2019年“创青春·交子杯”新网建行院校金融科技挑战赛-AI算法赛道的唇语数据集,该数据集是只有图片帧的数据集,整份数据集是由9996份带标签训练样本和2504份无标签预测样本构成,标签文件为txt格式文件,一个样本文件名对应一个英文熟语,存储量8GB左右。数据包含的唇语是两个字或则四个字的英文熟语,它们的比列为6816:3180,总共有313个类别,除了其中的“落地生根”和“卓有成效”两个类是只有22个样本,其他类别均有32个样本。每个数据样本的帧率由2到24不等,平均帧率在8帧左右,分布比较均匀,图片内容基本都是人头部的下半部份,如图所示。
图像浊度和饱和度都没有太大差别,这样一定程度缩减的干扰诱因,方便网路才能学习更多的有效特点。
数据可以分为有用信息和无用信息,在这份数据里,开口状态即为有用信息,闭口则是无用信息。因此在数据处理过程,神经网路应当注重注意开口状态的特点,但唇语数据中唇形差别并不大,使得低层数的网路提取不到更多有效的特点,所以在主干网路的选定上为多层数的深度3D神经网路,以便提取更多维度的特点提供网路学习记忆。此外,时间序列的特点信息也尤为重要,仅靠3D网路常常不会有太好的疗效,因此项目在分类网路结构中须要引入循环神经网路RNN,它能将最大限度的保留时序特点,非常符合网路对特点的需求。
4.2.目标测量算法
项目对比不同的目标测量算法进行唇部检查,yolov5s、Faster-RCNN、SSD300以及Yolov3-spp那些主流目标测量算法。不同算法与不同模型对同一份数据集的疗效肯定都是不一样的,通过试验不同算法在数据集中都进行了不同程度的训练以及测试,整理出了每一个算法在AP-50精度、GPU推理速率、置信度以及模型储存空间四个方面上的性能剖析,如表所示。
在项目设计中,目标测量模块须要的仅仅是推理速率上的高相应需求。在参考多份资料以及通过实验结果剖析,最终将算法的选择上采取Yolov5算法。
切割目标后:
4.3.分类网路
项目经过几轮测试对比,网络最终更改为ResNet内嵌一层的GRU,同样产生CNN与RNN的复合式网路,但网路并不会单纯使用3D的频域与池化的结构,因为在特点提取的过程中,更趋于于CNN来提取象素特点,让GRU来提取时间动态变化的特点。因此在方差模块中,卷积和池化将替换成2D操作,这一定程度上能防止时间维度带来的干扰。流程如图所示。
残差模块如下图所示。
由于网路处理的数据是由多帧图象构成的动态变化多维数据,如果仅仅采用3D卷积和池化等操作,会由于下取样过程中促使时间维度数据遗失。从数据集来说,每一个样本的帧率并不恒定,而且是从2帧到24帧不等,数据帧率平均在8帧左右,由于帧率过高,时间维度上的信息本身就存在过少状态。因此当采用3D的频域与池化后,会进一步削减该维度上的特点数据,所以网路最终会过于依赖图象的象素两个维度来进行强行拟合,这也许是不合理的。
当似然网路从3D的方差模块更换成2D以后,此时数据中的时间维度并不会进行下取样,所以该维度特点会得到一个挺好的保留。所以,为了提取时间维度的特点信息,在网路经过线性全联接以后引入一层门控循环单元GRU,让GRU通过更新门和重置门来控制时间维度特点信息的关联性。在全连接层中,通过输出的特点向量与隐藏层产生线性全联接,使网路融合了方差模块的特点信息来自适应体会向量的临界点,以此来提升网路的自适应表现能力。具体来说就是在线性全连接层中的引入自适应成语边界,不让GRU输出的隐藏层向量直接联接分类层,而是将GRU的每位时间维度的输出联接到全连接层中,在做sotfmax然后再将时间维度相乘,最后促使每位时间维度的输出都能为最后的分类层作出贡献声音识别算法源代码,最大化的实现了网路多维度的特点融合。
5.预处理trick
项目选择了100个成语进行研究,也就是100个类别,每个类别32份样本,一份样本平均帧率8帧左右,因此总体数据量有100×32×8=25600张图片左右。
根据标签与文件名生成词表,根据词表的下标对应各个类别。
5.1.数据增广
在图片的预处理中,首先是将图片进行缩放填充至112的象素,并对所有图象进行镜像翻转的数据提高,并对所有图象进行60%机率的双边混频去噪。如下图所示,a为原图,b是经过缩放、填充、双边混频后的图象,可以发觉相邻的象素显得愈发平滑,唇部棱角显得比较立体,图像的阴影更少了,这样很大程度降低了噪音的干扰。c图是镜像翻转的提高图象,可以使数据得到扩展,将3200份样本扩展到6400份。
5.2.帧数填充
由于网路仍然过拟合,通过观察数据集,发现这是因为数据集帧率不恒定而形成的。所以在分批次的时侯,会形成同一批次帧率不一的情况,这种情况有可能使网路会被帧率的干扰而影响特点提取的疗效。因此在预处理阶段,程序界定批次的时侯,就须要对每位批次进行0填充再训练,也就是将批次中每位样本的帧率固定到样本最大值,批次中样本统一帧率的方式输入到网路中训练。
6.效果展示 6.1.训练疗效
纯3DResnet18+预处理trick训练疗效:
3DDensenet+LSTM+GRU+预处理trick训练疗效:
3D+2D方差模块+GRU+预处理trick训练疗效:
不同预处理手段对比:
6.2.页面展示疗效
项目最终成品是页面测试离线视频,从切帧、目标定位切割、网络分类、页面展示结果。
点击Choose按键选择本地离线视频,选择后出现Predict按键,点击后进行视频处理,待网路处理完响应恳求,并将结果展示至页面,如下图。
7.弱点剖析
唇语辨识功能的测试中,会存在一个低泛化问题,这是因为数据集形成的。首先数据集中的样本是制作方取样而成,当训练出一个损失低正确率高,并且整体测试集疗效比较好的模型时,对自己录制的视频进行切帧并送入网路进行分类,会出现准确率低的情况,这很明显模型对于非数据集的测试样例预测的疗效并不是非常好。而模型的泛化能力想要提高,就须要对训练的数据集进行调整,例如降低一些自己的图象,增大每位类别的样本数据,以此提升模型的各项综合能力。
除了泛化问题,还存在相像成语辨识混淆问题。例如样本中“技术”和“基础”两个成语,它们之间存在高度相像的动态唇形,测试结果和想像的一样,模型有时会对类似成语未能正确地分类。如下图为系统对录制的“技术”、“基础”两个成语视频辨识结果。
在上图中,a图为“技术”词语,b图为“基础”词语,但是网路辨识结果却张冠李戴,将两词辨识互相混淆。如下图为二词唇部定位切割后的帧率图,首先从唇形上来说两者差别并不大,唯一不同的仅仅是“术”字和“础”字发音时的嚼舌情况不同。然而网路目前仅仅是针对图象的技术处理,并未涉及高层次的语义剖析,因此在碰到唇形相像的词句这些情况下,根本没法正确地区分类别。
混淆成语唇形切割:
在所使用的数据集中,类别的样本数不够很大程度上造成了这些情况形成,不仅这么,唇语辨识本来是一个语句输入的过程,前一个字与后一个字的关联性也取决了辨识的准确率高低。因此,在现有的基础上要想提升辨识疗效,首先须要从数据集出发,将过高帧率的样本进行重新取样,抽取高帧率的数据集样本,提高数据集的质量与科学性。同时还须要对网路深度进行更改,增加网路估算参数,让特点在网路中愈发细化。其次,应该在网路中引入注意力机制,让网路充分提取开口的象素特点,以及时间维度上帧与帧之间的联系。最后须要设计语义剖析模块,将图象转化词语义再对其特点向量做剖析处理,细化字与字之间的关联特点,并将数据映射到更高的维度上做到数据再分。
8.总结
针对web端操作下的唇语辨识系统,本文主要是使用了两大主流深度学习算法布署到Flask框架的集成思想,对如下内容进行了研究应用:
(1)Yolov5算法对人脸进行唇部定位,采用预测的座标对数据集进行处理,整理得到图象内容仅包含有效信息的数据集;
(2)在本文所使用的数据集中,对比了不同的目标测量算法与分类网路结构,通过实验数据剖析来最终确定选用的算法和网路结构;
(3)设计3DResNet和GRU复合网路,利用2D的方差模块组成深度网进行提取特点,最后借助GRU将每位帧率映射到特点维度中,形成由批次、时序和图象象素的高维度特点信息,再经过全联接层和softmax层处理;
(4)整合两个算法到Flask框架中,这是唇语辨识首次应用到web框架中,通过设计路由和URL地址,再配合视图函数对预测算法进行方式调用,让系统达到可视化疗效;
(5)对视频流进行预测,充分利用3D模型的优点对唇语视频进行辨识,从视频的读取到切帧,最后传入网路中对图象包含的唇语信息进行相关的解码,达到端到端的辨识疗效。
项目仍然存在一些不足的地方,针对这种问题,能从以下方面进行优化:第一是泛化能力,项目后续应当从数据集从发,需要进行一次所有类别的数据采集,以此来扩展样本数量。同时,在网路层数上,可以适当进行加深,以此来提高网路的表现能力,让数据愈发细分,模型能学习到更高维度的特点信息。第二是类别数量上,可以扩展至原数据集的313类,让项目就能辨识更多的英文熟语,让系统不受限于翻译的类别,让更多英文熟语甚至句子就能正确的被辨识。第三是响应时间里,模型有可能受笔记本的硬件设备影响,也有可能是双网路的诱因,导致处理时间过长,这应当是项目未来工作的重点研究对象,优化项目的各个细节,提升算法的执行能力。
此外,当前辨识的仅仅是成语,真正的唇语辨识应当是以短句的方式被翻译的,这就须要对视频进行句子判定,例如开口到闭口的停顿时长,是否可以借助这一点来做语句辨识的突破口。在辨识功能上,目前仅仅是读取离线视频辨识,未来的优化方向也可以向实时视频辨识挺进,让整个项目愈发的智能,更加人性化,攻克唇语辨识的这个困局,为将来的唇语工作作出积极的贡献。