精华内容
下载资源
问答
  • 基于solidworks2012制作的玩具四驱车,压缩包包括模型实验报告,零件截图
  • 数据分析与建模,实验报告,实验四,最优化模型建模分析 学生学号 实验...第 1 学期 1 实验报告填写说明 1 综合性设计性实验必须填写实验报告验证演示性实验可不写实验报告 2 实验报告书 必须按统一格式制作实验中心网站有
  • 用脚本制作粒子系统动画和类粒子动画。 1.根据视频教学制作喷泉动画,使用超级喷射,在侦听器中编写代码控制粒子动画。 2.根据视频教学制作类粒子动画,该动画虽然未使用3dsMax的粒子系统,但是由众多可以看作是粒子...
  • 疲劳检测实验报告

    2021-01-17 13:52:11
    文章目录疲劳检测实验报告——邢益玮前言一、最初的尝试——dlib库1.1 信息收集2.2 dlib库安装二、深度学习方法(基于keras)2.1 信息收集2.2 为Anaconda安装tensorflow+keras2.3 数据预处理(基于opencv)为...

    疲劳检测实验报告

    邢益玮 201930101151 2021/1/13

    (重度拖延症了,内容又有点多,学长和老师不好意思了🙏🙏🙏)



    前言

    实验目的
    本次探索性实验——疲劳检测的实验目的是通过计算机视觉,对于驾驶员的状态进行“疲劳”、“有点疲劳”、“清醒”三个状态的分类,其中需要实现无遮挡与戴口罩两种状态下的分类。

    准备工作:
    在第一周,我们所做的准备工作是录制自己在六种状态(有无口罩、疲劳程度)下的视频。
    我录制了不同状态共六段,每段长约一分钟的视屏文件,为了方便,之后的尝试都以我自己的六个视频作为数据集。

    实验平台:
    Python+Anaconda+spyder

    小背景:
    
    通过查阅资料,我发现在无遮挡状态下的疲劳检测已经有人用dlib库比较好的解决了,而戴口罩状态下的疲劳检测还尚待解决。
    最初,我想先尝试现有的dlib方案,然而安装的时候出了问题,dlib库用不了。
    不得已,我只能尝试另一条路——深度学习。
    
    然而在开始实验时,我的编程水平还只停留在大一上学期,只会一些python基础,对于深度学习,唯一的印象就是人工智能,热门。
    而它的相关知识,我一点都不知道,原理也更是一窍不通。
    于是我只能从零开始,去阅读各种网页,学习各种东西,尝试着一步一步走,遇到什么问题解决什么问题。
    
    由于无口罩状态下的分类已经较好的解决了,我后期的尝试主要专注于戴口罩状态下的分类。
    
    最终,耗时三四个星期,还是做出了一套“理论上可行”的疲劳检测方法。
    
    下面,我将逐步阐述我的实验过程
    

    以下是正文部分


    一、最初的尝试——dlib库

    1.1 信息收集

    首先查阅资料,搜索“python疲劳检测”等关键词。得到的结果中有许多方法,例如dlib、cnn等,然而完全看不懂。
    于是我试着阅读了老师与学长给的opencv教程等资料,但是依然没有什么头绪。
    直到我找到了几篇博客与视频,比较系统的阐述了dlib人脸识别的方法,再结合对于opencv的初步理解,我大概明白了利用dlib检测的大概流程。

    dlib库实现疲劳检测的方法:
    Dlib模型之驾驶员疲劳检测一(眨眼)
    dlib库检测人脸使用方法与简单的疲劳检测应用
    基于图像驾驶员疲劳检测技术研究fatigue_detecting

    dlib库进行检测的原理是通过人脸提取器,识别人脸68个特征点,然后提取坐标进行计算,通过眼部或嘴部特征点的距离判断是否眨眼或打哈欠,实现眨眼与打哈欠检测后再设置参数,当眨眼或打哈欠超过阈值,则认定为疲劳,并报警。有人还做了一个界面,将这个功能打包可视化。

    这一方法有两个不满足要求之处:一是无法区分疲劳与有点疲劳状态;二是在戴口罩状态下其检测效果未知。

    不过总的来说,这一方案已经很成熟了。

    于是在开始阶段,我当然是放弃了那些看不懂的cnn什么的,选择先去尝试这样一个原理相对简单的方法。

    1.2 为Anacoda安装opencv

    (当我把源码复制到初始的的spyder里面,点击运行,当然是没有成功的。)

    费了点功夫才知道,首先当然要安装dlib库与cv2(opencv)等库才能运行。于是上网百度安装方法。

    由于当时还不清楚ide与anaconda是两个环境,安装的时候踩了点坑

    在安装opencv的时候,我使用学长和同学提供的pip install方法安装成功。
    结果运行的时候,spyder提示“内核死亡”,无论怎么尝试都不行。
    于是又费了点功夫,才查到使用whl文件安装的办法。用whl文件安装成功。
    然而还是出现内核死亡的问题。查了不少资料,都没有这个情况。最后我花了一个小时重装了anaconda才解决。
    出现了问题

    安装opencv:
    Anaconda安装opencv-python
    windows环境下的Anaconda安装与OpenCV机器视觉环境搭建

    1.3 为Anacoda安装dlib

    安装dlib:
    Anaconda安装Dlib库
    Anaconda安装Dlib库(亲测成功)

    然而在dlib安装时,按照查阅到的资料,使用第一个方法——pip install,安装boost、cmake成功,但是安装dlib失败了,用系统给的的错误信息在百度搜索,也搜不到相关信息。

    然后就尝试使用第二个方法——whl文件安装。我使用从网上找到的whl文件进行安装(版本等都对应),虽然显示安装成功,但是在spyder内运行imoprt dlib时,依然出现错误,提示
    ImportError:DLL load failed:动态链接库(DLL)初始化失败
    在这里插入图片描述
    之后我重装anaconda,或是尝试把python版本降至3.7、3.6等,也添加了环境变量,都失败了,前后折腾了好几天。

    于是我只能尝试在百度上继续搜索错误,然后找到了如下网页:

    与dlib安装出错原因相关的网页:
    Tensorflow安装在windows 上面出现ImportError: DLL load failed: 动态链接库(DLL)初始化例程失败。

    有人在安装tensorflow时遇到了类似的错误,原因是cpu不支持avx指令集,而他安装的tensorflow版本需要调用avx指令集。

    依照我的硬件知识,我记得我电脑的cpu是g4560,恰好是不支持avx指令集的,而whl文件安装的dlib库应该是没有考虑过低端cpu不支持avx的问题的。所以我开始把重心放在dlib和avx上。

    有关dlib与avx指令集的网页:
    Dlib支持CPU指令集编译问题(SSE4.2或者AVX)
    Ubuntu 16.04开启dlib对于AVX或者CUDA的支持
    Dlib was compiled to use AVX instructions, but these aren’t available on your machine.
    Dlib安装遇到的坑

    最后搜集到的信息告诉我,大概要编译dlib才能解决这个问题。但是我完全没有接触过编译,一点概念都没有,也没有现成的博客可以套用。所以没办法,只能接着查。

    与编译dlib有关的网页:
    安装与编译Dlib(以Ubuntu16.04+Python3.6+pip为例)
    dlib库编译与使用
    Windows下的编译安装dlib(cmake)
    Win10下安装CMake3.14.2以及CMake使用教程

    最后我尝试着按照博客所教的,下载安装cmake并尝试进行dlib的编译,但是由于相关信息太少,最后还是没有成功。而且在使用cmake的过程中又出现了新的错误,导致连初始的几步都完成不了,只得作罢。

    由于安装dlib始终无法成功,我不得不考虑新的路线——深度学习。

    二、深度学习方法(基于keras)

    在经过一段时间的信息收集之后,我认识到疲劳检测问题本质上就是对不同状态进行分类的问题,而深度学习方法(cnn等)是十分适合解决分类问题的,网上也有很多现成的资源。在电脑装不上dlib库的情况下,我最终学习并尝试使用深度学习进行疲劳检测。

    2.1 信息收集

    由于最初收集信息的时候,我看到了有文章提到了使用cnn进行疲劳检测。在经过查询后我了解到cnn其实是卷积神经网络,属于深度学习的范畴,用在图片识别中。所以我的第一步是收集与cnn相关的疲劳检测方法,并尝试理解这一东西。

    基于cnn的疲劳检测方法:
    基于 CNN 的疲劳检测源码-Python
    基于MTCNN+CNN的疲劳(危险)驾驶检测系统设计
    GitHub - Revan-github/Fatigue-Driven-Detection-Based-on-CNN: 本科毕设内容:基于卷积神经网络的疲劳驾驶检测。

    结果我对着一堆源码和博客发呆了半天,完全看不懂(更别提这些源码有一些基于keras,有一些基于pytorch)。迫不得已,我准备从零开始学习深度学习的相关知识,以便读懂源码。于是我花了一两天时间,陆续阅读了这本教材的相关部分:

    Python深度学习
    (pdf版本是咸鱼花了几块钱买的,除了这本书之外还有一堆其他资源,懒得看了。)

    这本教材比较详细的介绍了深度学习的基本知识,然后介绍了深度学习的实践。

    我在里面主要参考了深度学习的基础内容与它在计算机视觉的运用,即卷积神经网络(cnn)的运用。读完之后我对其中的原理与语法有了一个大概的了解,而且我注意到一个省事的方法——使用预训练的卷积神经网络,即迁移学习。书上所举的例子使用vgg16网络进行特征提取(卷积),效果比最开始使用几个卷积层进行分类的效果好很多,并且所需要的数据集大大缩小。最重要的是我可以跳过最繁琐复杂的添加卷积层的步骤,而直接调用现成的模型。

    此时我产生了一条基本思路:
    将戴口罩下三个状态的视频分别按帧切分,产生三组图片——疲劳、有点疲劳、清醒,作为数据集。然后将这些数据集使用预训练模型进行训练,得到一个权重模型。在实际使用中,让摄像头隔一段时间拍一张人脸照,用这个权重模型去判断人脸照片的状态,进而通过一些参数的设定,确定驾驶员的状态。

    接下来是进一步搜集信息,进一步理解预训练模型:

    与预训练模型有关的文章:
    卷积神经网络VGG16这么简单,为什么没人能说清?
    mlnd_distracted_driver_detection
    【转载】ResNet论文笔记((对比vgg16网络))
    学习笔记:大话经典模型AlexNet、VGGNet、GoogLeNet、ResNet
    老司机坐稳了,Kaggle竞赛-深度学习检测疲劳驾驶简要回顾

    在对书中介绍的几种模型进行了解后,我选择了resnet50模型,理由是它在imagenet比赛中打败其他众多模型,精确度较高,而且在keras中可以由一行代码很方便的调用。

    接下来是搜集有关resnet50模型运用在图像分类与迁移学习中的示例进行参考。但是在这一步中,我搜索到的示例所使用的数据集格式都不是图片,而是hdf5格式,于是我又搜索了一些通过图片制作数据集的文献,不过最后没有用到。

    制作数据集:
    keras库学习——生成一个自己的数据集
    Python将自己的图片数据集导入h5py,做识别的预处理

    最后解决问题的是两篇博客,也是之后工作的模板与参考的重点:

    (新手向)keras resnet 训练自己的数据集 图像分类
    利用python+opencv 读取视频的每一帧并保存

    在使用这两篇博客的内容完成主体模型的构建工作后,剩下的内容就是一些改进、完善与小修小补了。

    下面,我将介绍实现整一套模型构建的流程,并给出源码

    2.2 为Anaconda安装tensorflow+keras

    在读完书并做了一些其他的理论准备后,我尝试搭建了自己的keras深度学习平台,并且在上面成功的运行了书上示例的mnist手写数据集的训练。(顺便一提,我的电脑显卡是1060 6g,刚好是深度学习推荐配置的最低要求,这也是我选择尝试的一个重要前提)

    以下是我的配置与安装的各个版本:

    显卡:gtx1060 6g
    驱动程序版本:442.50
    python:3.7.9
    opencv:4.4.0
    CUDA:10.0.130(与博客相同)
    cudnn:v7.6.4(与博客相同)
    tensorflow-gpu:1.15.0(与博客相同)
    keras:2.3.1
    spyder:4.1.5
    

    安装tensorflow:
    使用anaconda 3安装tensorflow 1.15.0 (win10环境)
    安装keras:
    tensorflow和keras版本对应关系
    conda创建TensorFlow和Keras指定版本环境
    安装spyder:
    WIN10+Anaconda3+spyder安装tensorflow避坑攻略
    在windows Anaconda中安装tensorflow 以及安装spyder(在Spyder中编辑tensorflow代码)

    整个流程中最麻烦,最难的就是安装tensorflow。创建新环境,安装cuda,搞了一大堆,非常耗时。我当时还完全搞不懂创建环境等操作(甚至不知道在prompt里要先要激活环境),还重装了驱动,感觉整个安装的过程比较繁琐。而且后来遇到了一些问题,折腾了一个下午才装好tensorflow。不过装好以后还是挺有成就感的,毕竟dlib折腾了一星期还是安不上QAQ

    安装keras就比较简单了,直接一行命令搞定。

    装好keras之后还要在环境中重新安装spyder,试了两种方法才成功,也装了挺久。

    但是搞完之后出现在spyder中无法import tf、keras版本过高等问题。我又按照当时查到的资料,在prompt里面重装hdf5库,不行;又重装spyder,也不行。最后查到了另一篇资料,升级conda库解决,成功安装。

    装好keras之后,我尝试运行了书中示例:mnist手写数据集,成功。

    minist手写数据集.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Fri Oct 30 15:53:02 2020
    @author: xyw
    """
    from keras.datasets import mnist
    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()
    from keras import models
    from keras import layers
    network = models.Sequential()
    network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
    network.add(layers.Dense(10, activation='softmax'))
    network.compile(optimizer='rmsprop',
    loss='categorical_crossentropy',
    metrics=['accuracy'])
    train_images = train_images.reshape((60000, 28 * 28))
    train_images = train_images.astype('float32') / 255
    test_images = test_images.reshape((10000, 28 * 28))
    test_images = test_images.astype('float32') / 255
    from keras.utils import to_categorical
    train_labels = to_categorical(train_labels)
    test_labels = to_categorical(test_labels)
    network.fit(train_images, train_labels, epochs=5, batch_size=128)
    

    运行结果:

    60000/60000 [==============================] - 2s 31us/step - loss: 0.2558 - accuracy: 0.9247
    Epoch 2/5
    60000/60000 [==============================] - 1s 21us/step - loss: 0.1041 - accuracy: 0.9700
    Epoch 3/5
    60000/60000 [==============================] - 1s 21us/step - loss: 0.0685 - accuracy: 0.9795
    Epoch 4/5
    60000/60000 [==============================] - 1s 21us/step - loss: 0.0488 - accuracy: 0.9855
    Epoch 5/5
    60000/60000 [==============================] - 1s 21us/step - loss: 0.0375 - accuracy: 0.9885
    

    成功之后我非常开心。至此,能够尝试新的方向了。

    接下来就是尝试疲劳检测模型的训练了。

    2.3 数据预处理(基于opencv)

    利用python+opencv 读取视频的每一帧并保存

    我将目标放在戴口罩状态下的疲劳检测上,依照基本思路,我首先需要将戴口罩的三个视频按帧提取,得到戴口罩状态下的三组照片,再进行下一步操作。

    opencv视频转图片.py
    
    import cv2
    import os
    #要提取视频的文件名,隐藏后缀
    sourceFileName='3'
    #在这里把后缀接上
    video_path = os.path.join("C:\\Users\\Administrator\\Desktop\\pilaojiancexyw\\", sourceFileName+'.mp4')
    times=0
    #提取视频的频率,每1帧提取一个
    frameFrequency=1
    #输出图片到当前目录vedio文件夹下
    outPutDirName='vedio/'+sourceFileName+'/'
    if not os.path.exists(outPutDirName):
        #如果文件目录不存在则创建目录
        os.makedirs(outPutDirName)
    camera = cv2.VideoCapture(video_path)
    while True:
        times+=1
        res, image = camera.read()
        if not res:
            print('not res , not image')
            break
        if times%frameFrequency==0:
            cv2.imwrite(outPutDirName + str(times)+'.jpg', image)
            print(outPutDirName + str(times)+'.jpg')
    print('图片提取结束')
    camera.release()
    

    利用这个代码,我将戴口罩下三个视频转为图片,放在了桌面的vedio文件夹中。下面给出文件夹的部分截图:

    在这里插入图片描述
    上面的文件夹分别是原始视频内容、存放处理后图片的文件夹、处理后的图片的部分示例。基本概况如下:

    定义:
    文件夹1(视频1):有口罩精神:视频文件1分01秒,60fps\图片922张
    文件夹2(视频2):有口罩有点困:视频文件1分01秒,60fps\图片920张
    文件夹3(视频3):有口罩很困:视频文件1分06秒,60fps\图片993张
    尺寸均为:1920×1080
    

    也就是说,我的数据集共约有3000张图片,这个数据集大小基本符合训练的条件。

    但是这个数据集的背景、光线、人物等图片要素都比较相似。因此普适性很差,训练出来的模型肯定鲁棒性弱。如果要进行进一步的改进,就要收集更多的不同人、不同拍摄环境下的图片,并且可以进行数据增强。但是这些以我目前的条件很难采集,就暂且放弃了。

    完成了数据的预处理之后,就要开始正式的工作了。

    2.4 二分类(基于resnet50)

    在这一步,我开始尝试使用该博客的代码进行二分类:

    (新手向)keras resnet 训练自己的数据集 图像分类

    2.4.1 数据集制作

    该博客使用的数据集由四个部分组成,分别是:X_train,Y_train,X_test,Y_test
    意义是X、Y二分类,每类的数据集再划分为训练与测试集。这四个数据集分别对应四个存放图片的文件夹。

    仿照这样的数据结构,我建立了四个文件夹,内容为:

    train\qingxing:文件夹1的前500张
    train\kun:2前500张、3前500张
    test\qingxing:文件夹1的后面全部
    test\kun :2的后面全部、3的后面全部
    

    由于博客是二分类,我就将有点困、很困两种状态一并加到kun这一类里了。事实证明,这样做是不对的。当我训练出模型后,我发现不论是哪种状态,最后都会判定为一类。我在这篇博客的评论区找到了问题所在。
    在这里插入图片描述
    于是我手动将kun的train样本随机删去一部分,让两个状态的样本量变为:

    train\qingxing:544张图片
    train\kun:561张图片
    test\qingxing:378张图片
    test\kun :360张图片
    

    这样处理之后,训练的模型能够正常区分两种状态了。

    接下来是制作数据集。在博客中,博主在完整代码中直接读取文件夹的文件,将其转为X_train,Y_train,X_test,Y_test这四个numpy数组(array),然后直接用它进行下一步的训练,并没有保存这四个数组。而在实际运行中,读取数据图片并处理转化为数组需要一定时间。为了方便后续工作,我将制作数组的代码单独改写,保存最后得到的四个数组,并在后续直接读取使用。

    数据集制作.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Tue Nov 10 11:55:52 2020
    @author: xyw
    """
    import os
    import numpy as np
    
    from tensorflow.keras.preprocessing import image
    
    from PIL import Image
    import random
    
    def DataSet():
        # 首先需要定义训练集和测试集的路径,这里创建了 train , 和 test 文件夹
        # 每个文件夹下又创建了 glue,medicine 两个文件夹,所以这里一共四个路径
        train_path_k ='C:\\Users\\Administrator\\Desktop\\vedio\\train\\kun\\'
        train_path_qx = 'C:\\Users\\Administrator\\Desktop\\vedio\\train\\qingxing\\'
        
        test_path_k ='C:\\Users\\Administrator\\Desktop\\vedio\\test\\kun\\'
        test_path_qx = 'C:\\Users\\Administrator\\Desktop\\vedio\\test\\qingxing\\'
        
        # os.listdir(path) 是 python 中的函数,它会列出 path 下的所有文件名
        # 比如说 imglist_train_glue 对象就包括了/train/glue/ 路径下所有的图片文件名
        imglist_train_k = os. listdir(train_path_k)
        imglist_train_qx = os.listdir(train_path_qx)
        
        # 下面两行代码读取了 /test/glue 和 /test/medicine 下的所有图片文件名
        imglist_test_k= os.listdir(test_path_k)
        imglist_test_qx = os.listdir(test_path_qx)
        
        # 这里定义两个 numpy 对象,X_train 和 Y_train
        
        # X_train 对象用来存放训练集的图片。每张图片都需要转换成 numpy 向量形式
        # X_train 的 shape 是 (360,224,224,3) 
        # 360 是训练集中图片的数量(训练集中固体胶和西瓜霜图片数量之和)
        # 因为 resnet 要求输入的图片尺寸是 (224,224) , 所以要设置成相同大小(也可以设置成其它大小,参看 keras 的文档)
        # 3 是图片的通道数(rgb)
        
        # Y_train 用来存放训练集中每张图片对应的标签
        # Y_train 的 shape 是 (360,2)
        # 360 是训练集中图片的数量(训练集中固体胶和西瓜霜图片数量之和)
        # 因为一共有两种图片,所以第二个维度设置为 2
        # Y_train 大概是这样的数据 [[0,1],[0,1],[1,0],[0,1],...]
        # [0,1] 就是一张图片的标签,这里设置 [1,0] 代表 固体胶,[0,1] 代表西瓜霜
        # 如果你有三类图片 Y_train 就因该设置为 (your_train_size,3)
        
        X_train = np.empty((len(imglist_train_k) + len(imglist_train_qx), 224, 224, 3))
        Y_train = np.empty((len(imglist_train_k) + len(imglist_train_qx), 2))
        
        # count 对象用来计数,每添加一张图片便加 1
        count1 = 0
        # 遍历 /train/glue 下所有图片,即训练集下所有的固体胶图片
        for img_name in imglist_train_k:
            # 得到图片的路径
            img_path = train_path_k + img_name
            # 通过 image.load_img() 函数读取对应的图片,并转换成目标大小
            #  image 是 tensorflow.keras.preprocessing 中的一个对象
            img = image.load_img(img_path, target_size=(224, 224))
            # 将图片转换成 numpy 数组,并除以 255 ,归一化
            # 转换之后 img 的 shape 是 (224,224,3)
            img = image.img_to_array(img) / 255.0
            
            # 将处理好的图片装进定义好的 X_train 对象中
            X_train[count1] = img
            # 将对应的标签装进 Y_train 对象中,这里都是 固体胶(glue)图片,所以标签设为 [1,0]
            Y_train[count1] = np.array((1,0))
            count1+=1
        # 遍历 /train/medicine 下所有图片,即训练集下所有的西瓜霜图片
        for img_name in imglist_train_qx:
    
            img_path = train_path_qx + img_name
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            
            X_train[count1] = img
            Y_train[count1] = np.array((0,1))
            count1+=1
            
        # 下面的代码是准备测试集的数据,与上面的内容完全相同,这里不再赘述
        X_test = np.empty((len(imglist_test_k) + len(imglist_test_qx), 224, 224, 3))
        Y_test = np.empty((len(imglist_test_k) + len(imglist_test_qx), 2))
        count2 = 0
        for img_name in imglist_test_k:
    
            img_path = test_path_k + img_name
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            
            X_test[count2] = img
            Y_test[count2] = np.array((1,0))
            count2+=1
        for img_name in imglist_test_qx:
            
            img_path = test_path_qx + img_name
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            
            X_test[count2] = img
            Y_test[count2] = np.array((0,1))
            count2+=1
            
    	# 打乱训练集中的数据
        index = [i for i in range(len(X_train))]
        random.shuffle(index)
        X_train = X_train[index]
        Y_train = Y_train[index]
        
        # 打乱测试集中的数据
        index = [i for i in range(len(X_test))]
        random.shuffle(index)
        X_test = X_test[index]    
        Y_test = Y_test[index]	
    
        return X_train,Y_train,X_test,Y_test
    
    X_train,Y_train,X_test,Y_test = DataSet()
    
    np.save('C:\\Users\\Administrator\\Desktop\\test2\\X_train', X_train, allow_pickle=True, fix_imports=True)
    np.save('C:\\Users\\Administrator\\Desktop\\test2\\Y_train', Y_train, allow_pickle=True, fix_imports=True)
    np.save('C:\\Users\\Administrator\\Desktop\\test2\\X_test', X_test, allow_pickle=True, fix_imports=True)
    np.save('C:\\Users\\Administrator\\Desktop\\test2\\Y_test', Y_test, allow_pickle=True, fix_imports=True)
    
    print('X_train shape : ',X_train.shape)
    print('Y_train shape : ',Y_train.shape)
    print('X_test shape : ',X_test.shape)
    print('Y_test shape : ',Y_test.shape)
    

    运行结果如下:

    X_train shape :  (1105, 224, 224, 3)
    Y_train shape :  (1105, 2)
    X_test shape :  (738, 224, 224, 3)
    Y_test shape :  (738, 2)
    

    在test2文件夹中的数据集:
    在这里插入图片描述

    数据集维度定义:
    训练集:
    	X_train:1105张图片,尺寸224×224,颜色通道rgb共3个
    	Y_train:1105张图片,2个标签
    	(X_train、Y_train的图片与标签维度在顺序上是一一对应的)
    测试集:
    	X_test、Y_test同理。
    
    标签:
    	kun:(1,0)
    	qingxing:(0,1)
    	
    在代码中将图片尺寸进行了缩放(1920×1080 --> 224×224)以适应resnet50模型的需要。
    

    得到了四个数据集文件之后,接下来就可以开展训练模型与检验模型的工作了。

    2.4.2 训练模型

    先给出训练模型的代码:

    resnet训练模型.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Tue Nov 10 12:03:59 2020
    @author: xyw
    """
    import numpy as np
    import scipy
    from scipy import ndimage
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from tensorflow.keras.applications.resnet50 import ResNet50
    from tensorflow.keras.preprocessing import image
    from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
    
    
    # # model
    X_train = np.load('C:\\Users\\Administrator\\Desktop\\test2\\X_train.npy')
    Y_train = np.load('C:\\Users\\Administrator\\Desktop\\test2\\Y_train.npy')
    X_test = np.load('C:\\Users\\Administrator\\Desktop\\test2\\X_test.npy')
    Y_test = np.load('C:\\Users\\Administrator\\Desktop\\test2\\Y_test.npy')
    
    model = ResNet50(weights=None,classes=2)
    
    model.compile(optimizer=tf.train.AdamOptimizer(0.001),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    
    # # train
    history=model.fit(X_train, Y_train, epochs=10, batch_size=12,validation_data=(X_test, Y_test))
    
    # # evaluate
    model.evaluate(X_test, Y_test, batch_size=32)
    
    # # save
    model.save('C:\\Users\\Administrator\\Desktop\\test2\\my_resnet_model_e10bs12.h5')
    
    # # draw
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    

    在训练模型的时候,我更改调试的参数为epochs(训练轮数,一般往多了设)、batch_size(越大越好,但是太大会爆显存,我试了batch_size=32,确实爆了)

    参数解释:
    深度学习中的Epoch,Batchsize,Iterations,都是什么鬼?

    在生成文件时,依据这两个参数的大小,定义命名规则如下:

    my_resnet_model_e'x'bs'y'
    其中'x'是epochs的大小,'y'是batch_size的大小
    

    我改变参数训练了几次,得到以下几个模型文件:
    在这里插入图片描述
    它们训练的结果如下:

    e5bs12
    1105/1105 [==============================] - 30s 27ms/sample - loss: 0.4514 - acc: 0.8805
    Epoch 2/5
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.2093 - acc: 0.9376
    Epoch 3/5
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.1699 - acc: 0.9439
    Epoch 4/5
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.0576 - acc: 0.9846
    Epoch 5/5
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.0111 - acc: 0.9964
    738/738 [==============================] - 6s 8ms/sample - loss: 8.0788 - acc: 0.4878
    
    e1bs12
    1105/1105 [==============================] - 30s 27ms/sample - loss: 0.3237 - acc: 0.9104
    738/738 [==============================] - 6s 8ms/sample - loss: 7.0154 - acc: 0.4878
    
    e6bs12
    1105/1105 [==============================] - 30s 27ms/sample - loss: 0.4012 - acc: 0.8860
    Epoch 2/6
    1105/1105 [==============================] - 22s 20ms/sample - loss: 0.2523 - acc: 0.9222
    Epoch 3/6
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.1121 - acc: 0.9538
    Epoch 4/6
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.0404 - acc: 0.9864
    Epoch 5/6
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.1458 - acc: 0.9529
    Epoch 6/6
    1105/1105 [==============================] - 23s 20ms/sample - loss: 0.0742 - acc: 0.9756
    738/738 [==============================] - 6s 8ms/sample - loss: 0.6280 - acc: 0.8347
    
    e10bs12
    1105/1105 [==============================] - 36s 32ms/sample - loss: 0.4967 - acc: 0.8661 - val_loss: 4.1914 - val_acc: 0.4878
    Epoch 2/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.1754 - acc: 0.9348 - val_loss: 22.9513 - val_acc: 0.4878
    Epoch 3/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.1459 - acc: 0.9566 - val_loss: 6.2329 - val_acc: 0.4878
    Epoch 4/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.1808 - acc: 0.9520 - val_loss: 0.8540 - val_acc: 0.6707
    Epoch 5/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.1071 - acc: 0.9647 - val_loss: 3.1942 - val_acc: 0.5854
    Epoch 6/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.1488 - acc: 0.9629 - val_loss: 1.3391 - val_acc: 0.6802
    Epoch 7/10
    1105/1105 [==============================] - 27s 25ms/sample - loss: 0.0722 - acc: 0.9810 - val_loss: 0.7883 - val_acc: 0.6003
    Epoch 8/10
    1105/1105 [==============================] - 28s 25ms/sample - loss: 0.0126 - acc: 0.9973 - val_loss: 3.1924 - val_acc: 0.6003
    Epoch 9/10
    1105/1105 [==============================] - 28s 25ms/sample - loss: 0.0189 - acc: 0.9919 - val_loss: 1.0980 - val_acc: 0.6504
    Epoch 10/10
    1105/1105 [==============================] - 28s 25ms/sample - loss: 0.0078 - acc: 0.9982 - val_loss: 3.8652 - val_acc: 0.5000
    738/738 [==============================] - 5s 7ms/sample - loss: 3.8652 - acc: 0.5000
    
    参数解释:
    acc:代表模型分类图片的准确度,越高越好
    loss:大意是模型输出与真实值的误差,是损失函数的值(例如交叉熵函数),越低越好
    

    其中acc与loss是在训练集上训练模型的结果,val_acc与val_loss是在测试集上测试模型的结果。在结果中还能看出训练一轮的时间。

    最好的一次训练结果,在训练集上训练的精度达到0.96,而在测试集上测试的精度达到0.68,不大高。出现这个结果的原因与我的数据集制作有关,在制作数据集的过程中,样本不够大,而且测试集与训练集的划分是手工随机的,并没有做到完全随机划分。但即便是在这样的情况下,在测试集上的测试精度也能接近百分之七十,勉强能用,也间接表明了resnet50模型的实用性不错。

    在训练完前几个模型后,我用教材中的代码完善了博客的代码,使得输出结果可以展示模型在测试集上测试的结果,并且做出acc与loss变化图,通过这些图片可以直观的感受训练过程中的acc与loss变化的趋势。如下是在epochs=10, batch_size=12的一次训练中生成的图片:
    e10bs12-acc.png
    e10bs12-loss.png
    在训练并保存模型文件之后,就要看看模型文件如何调用,如何应用于实际的检测了。

    2.4.3 调用模型

    先给代码:

    测试模型.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Tue Nov 10 11:34:39 2020
    @author: xyw
    """
    import numpy as np
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from tensorflow.keras.preprocessing import image
    
    model = tf.keras.models.load_model('C:\\Users\\Administrator\\Desktop\\test2\\my_resnet_model_e10bs12.h5')
    
    # # test
    img_path = "C:\\Users\\Administrator\\Desktop\\vedio\\2\\338.jpg"
    #测试的图片的路径
    img = image.load_img(img_path, target_size=(224, 224))
    #需要将测试用的1080p的原图缩放为模型匹配的224×224大小图片
    
    plt.imshow(img)
    img = image.img_to_array(img)/ 255.0
    img = np.expand_dims(img, axis=0)  # 为batch添加第四维
    
    print(model.predict(img))    #keras调用模型只需要一个predict函数,非常方便
    

    运行的结果与原图比较如下:
    在这里插入图片描述

    从运行结果可以看出,这张照片的标签被判定为(1,0),属于困。而我选择的图片正是来自有点困状态的文件夹,模型的判断是正确的。

    进行到这一步,利用深度学习进行疲劳检测已经取得了初步成功。

    2.5 三分类(基于resnet50)

    测试二分类成功后,我接着朝着最初的三分类方向改进模型。在博客中已经提到三分类的办法,但是还没有具体 实现。有了二分类的经验,我将模型改为三分类也没有遇到太大的困难。

    (新手向)keras resnet 训练自己的数据集 图像分类

    2.5.1 数据集制作

    先给代码:

    数据集制作_三分类.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Thu Nov 12 12:34:14 2020
    @author: xyw
    """
    import os
    import numpy as np
    from tensorflow.keras.preprocessing import image
    from PIL import Image
    import random
    
    def DataSet():
        train_path_k ='C:\\Users\\Administrator\\Desktop\\vedio\\3\\'
        train_path_ydk ='C:\\Users\\Administrator\\Desktop\\vedio\\2\\'
        train_path_qx = 'C:\\Users\\Administrator\\Desktop\\vedio\\1\\'
        imglist_train_k = os. listdir(train_path_k)
        imglist_train_ydk = os. listdir(train_path_ydk)
        imglist_train_qx = os.listdir(train_path_qx)
        #读取文件
        
        X_train = np.empty((len(imglist_train_k) + len(imglist_train_qx) + len(imglist_train_ydk), 224, 224, 3))
        Y_train = np.empty((len(imglist_train_k) + len(imglist_train_qx) + len(imglist_train_ydk), 3))
        #创建numpy对象,由于是三分类,第四维是3,有三个标签
        
        count1 = 0
        # count 对象用来计数,每添加一张图片便加 1
        
        # 遍历困状态下的所有图片并写入训练集
        for img_name in imglist_train_k:
            img_path = train_path_k + img_name
            # 通过 image.load_img() 函数读取对应的图片,并转换成目标大小
            #  image 是 tensorflow.keras.preprocessing 中的一个对象
            img = image.load_img(img_path, target_size=(224, 224))
            # 将图片转换成 numpy 数组,并除以 255 ,归一化
            # 转换之后 img 的 shape 是 (224,224,3)
            img = image.img_to_array(img) / 255.0
            # 将处理好的图片装进定义好的 X_train 对象中
            X_train[count1] = img
            # 将对应的标签装进 Y_train 对象中,困的标签设为(0,0,1)
            Y_train[count1] = np.array((0,0,1))
            count1+=1    #计数器+1
            
        # 遍历清醒状态的所有图片并写入训练集
        for img_name in imglist_train_qx:
        
            img_path = train_path_qx + img_name
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            
            X_train[count1] = img
            Y_train[count1] = np.array((1,0,0))
            count1+=1
            
        #遍历有点困状态下所有图片并写入训练集
        for img_name in imglist_train_ydk:
    
            img_path = train_path_ydk + img_name
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            
            X_train[count1] = img
            Y_train[count1] = np.array((0,1,0))
            count1+=1        
            
        # 不准备测试集,已经将所有图片写入训练集中
        
    	# 打乱训练集中的数据
        index = [i for i in range(len(X_train))]
        random.shuffle(index)
        X_train = X_train[index]
        Y_train = Y_train[index]
       
        return X_train,Y_train    #Dataset()函数返回两个训练集
    
    X_train,Y_train = DataSet()
    
    np.save('C:\\Users\\Administrator\\Desktop\\test3\\X_train', X_train, allow_pickle=True, fix_imports=True)
    np.save('C:\\Users\\Administrator\\Desktop\\test3\\Y_train', Y_train, allow_pickle=True, fix_imports=True)
    #保存数据集
    
    print('X_train shape : ',X_train.shape)
    print('Y_train shape : ',Y_train.shape)
    
    

    输出结果:

    X_train shape :  (2835, 224, 224, 3)
    Y_train shape :  (2835, 3)
    

    生成的文件:
    在这里插入图片描述
    与二分类时的操作不同,在三分类中,我查阅资料,在调用model.fit()训练模型时,通过validation_split参数来指定从数据集中切分出验证集的比例。也就是让系统在训练时自动将训练集划分一部分作为测试集。这样可以避免像二分类时那样手动划分训练集与测试集,使数据集的划分随机性更强,而且省事。因此我在数据集制作的时候将所有的图片都打包在一个数据集中。

    数据集维度定义:
    	X_train:2835张图片,尺寸224×224,颜色通道rgb3个
    	Y_train:2835张图片,3个标签
    标签:
    	清醒:qx,(1,0,0), 类别0,图片来自文件夹1
    	有点困:ydk,(0,1,0 ),类别1,图片来自文件夹2
    	困:k,(0,0,1),类别2,图片来自文件夹3
    

    完成数据集制作之后,接下来进行模型的训练。

    2.5.2 训练模型

    先给代码:

    resnet训练模型_三分类.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Thu Nov 12 12:56:57 2020
    @author: xyw
    """
    import numpy as np
    import scipy
    from scipy import ndimage
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from tensorflow.keras.applications.resnet50 import ResNet50
    from tensorflow.keras.preprocessing import image
    from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
    from keras.callbacks import ModelCheckpoint
    
    # # model
    X_train = np.load('C:\\Users\\Administrator\\Desktop\\test3\\X_train.npy')
    Y_train = np.load('C:\\Users\\Administrator\\Desktop\\test3\\Y_train.npy')
    
    model = ResNet50(weights=None,classes=3)
    
    model.compile(optimizer=tf.train.AdamOptimizer(0.001),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    
    # # 保存模型路径
    filepath='C:\\Users\\Administrator\\Desktop\\test3\\classes=3_times=1_e30bs12\\myresnet50model_3classes_times=1_e30bs12_etimes={epoch:02d}_valacc={val_acc:.2f}.h5'
    
    # # # 需要按命名规则修改文件名(第一处需要修改的地方):classes times epoches batchsize etimes valacc
    # # # 需要自己手动按命名规则创建文件夹来存放模型
    
    # 第一种保存方式:有一次提升, 则保存一次.
    #checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1,save_best_only=True,mode='max')
    #callbacks_list = [checkpoint]
     
    # 第二种保存方式:保存每一次(我采用的方法)
    checkpoint = ModelCheckpoint(filepath, verbose=1,save_best_only=False,save_weights_only=False)
    callbacks_list = [checkpoint]
    
    # # train
    history=model.fit(X_train, Y_train, epochs=30, batch_size=12,validation_split=0.25,callbacks=callbacks_list)
    
    # # #可以修改的参数(第二个需要改的地方):epoches batchsize validation_split
    # # evaluate
    # # 第三种保存方式:仅保存最终模型
    #model.save('C:\\Users\\Administrator\\Desktop\\test3\\myresnet50model_3classes_times=1_e30bs1_{epoch:02d}etimes_valacc={val_acc:.2f}.h5')
    # # # 按命名规则修改(第三个需要改的地方):classes times epoches batchsize etimes valacc
    
    # # draw 并保存图片到文件夹下(手动另存为)
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    
    
    命名规则(方便后续整理):
    
    例如test3\\classes=3_times=1_e30bs12\\myresnet50model_3classes_times=1_e30bs12_etimes={epoch:02d}_valacc={val_acc:.2f}.h5',其中最后两个参数由模型的callback函数提供,前面的参数由模型参数手动更改。
    
    代表模型存在test3\\classes=3_times=1_e30bs12文件夹下(文件夹需要手动建),三分类,第一次在该参数下进行训练,epochs=30, batch_size=12。
    
    模型的名字myresnet50model_3classes_times=1_e30bs12_etimes=01_valacc=0.32,代表三分类,第一次在该参数下训练得到,epochs=30, batch_size=12,训练的轮数为01,测试集精度为0.32
    

    运行结果(选择了最好的一次):

    参数:epochs=30, batch_size=12,validation_split=0.25(划分0.25的数据来作为测试集)
    
    2126/2126 [==============================] - 76s 36ms/sample - loss: 0.5426 - acc: 0.8269 - val_loss: 8.7729 - val_acc: 0.3159
    
    Epoch 2/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.2788 - acc: 0.9101 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.2799 - acc: 0.9097 - val_loss: 2.2470 - val_acc: 0.4175
    
    Epoch 3/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1219 - acc: 0.9586 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1218 - acc: 0.9586 - val_loss: 1.3937 - val_acc: 0.5910
    
    Epoch 4/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1075 - acc: 0.9670 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1075 - acc: 0.9671 - val_loss: 0.6032 - val_acc: 0.8463
    
    Epoch 5/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1129 - acc: 0.9600 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1139 - acc: 0.9595 - val_loss: 0.1106 - val_acc: 0.9704
    
    Epoch 6/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1221 - acc: 0.9647 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1222 - acc: 0.9647 - val_loss: 0.2295 - val_acc: 0.8942
    
    Epoch 7/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0609 - acc: 0.9783 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0622 - acc: 0.9779 - val_loss: 0.9590 - val_acc: 0.8322
    
    Epoch 8/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1305 - acc: 0.9647 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1305 - acc: 0.9647 - val_loss: 0.3354 - val_acc: 0.9055
    
    Epoch 9/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0188 - acc: 0.9944 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0214 - acc: 0.9939 - val_loss: 0.4358 - val_acc: 0.9013
    
    Epoch 10/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0880 - acc: 0.9727 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0879 - acc: 0.9727 - val_loss: 1.8988 - val_acc: 0.7109
    
    Epoch 11/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0343 - acc: 0.9896 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0343 - acc: 0.9897 - val_loss: 0.1955 - val_acc: 0.9140
    
    Epoch 12/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0025 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0025 - acc: 1.0000 - val_loss: 6.6829e-04 - val_acc: 1.0000
    
    Epoch 13/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0143 - acc: 0.9944     
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0143 - acc: 0.9944 - val_loss: 2.2343 - val_acc: 0.4034
    
    Epoch 14/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1135 - acc: 0.9609 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1134 - acc: 0.9610 - val_loss: 23.4646 - val_acc: 0.3441
    
    Epoch 15/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0151 - acc: 0.9972 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0151 - acc: 0.9972 - val_loss: 0.0014 - val_acc: 1.0000
    
    Epoch 16/30
    2124/2126 [============================>.] - ETA: 0s - loss: 8.6257e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 8.6390e-04 - acc: 1.0000 - val_loss: 7.1752e-04 - val_acc: 1.0000
    
    Epoch 17/30
    2124/2126 [============================>.] - ETA: 0s - loss: 4.7914e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 4.9877e-04 - acc: 1.0000 - val_loss: 1.6481e-04 - val_acc: 1.0000
    
    Epoch 18/30
    2124/2126 [============================>.] - ETA: 0s - loss: 2.7230e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 2.7305e-04 - acc: 1.0000 - val_loss: 9.6378e-05 - val_acc: 1.0000
    
    Epoch 19/30
    2124/2126 [============================>.] - ETA: 0s - loss: 1.0641e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 1.0662e-04 - acc: 1.0000 - val_loss: 8.4853e-05 - val_acc: 1.0000
    
    Epoch 20/30
    2124/2126 [============================>.] - ETA: 0s - loss: 1.4195e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 1.4551e-04 - acc: 1.0000 - val_loss: 1.1583e-04 - val_acc: 1.0000
    
    Epoch 21/30
    2124/2126 [============================>.] - ETA: 0s - loss: 6.5002e-05 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 6.4958e-05 - acc: 1.0000 - val_loss: 8.6277e-05 - val_acc: 1.0000
    
    Epoch 22/30
    2124/2126 [============================>.] - ETA: 0s - loss: 8.2017e-05 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 8.2002e-05 - acc: 1.0000 - val_loss: 7.6832e-05 - val_acc: 1.0000
    
    Epoch 23/30
    2124/2126 [============================>.] - ETA: 0s - loss: 4.4426e-05 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 4.7932e-05 - acc: 1.0000 - val_loss: 6.7494e-05 - val_acc: 1.0000
    
    Epoch 24/30
    2124/2126 [============================>.] - ETA: 0s - loss: 4.0728e-05 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 4.0696e-05 - acc: 1.0000 - val_loss: 8.3068e-05 - val_acc: 1.0000
    
    Epoch 25/30
    2124/2126 [============================>.] - ETA: 0s - loss: 3.0508e-04 - acc: 1.0000 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 3.5365e-04 - acc: 1.0000 - val_loss: 5.0472e-04 - val_acc: 1.0000
    
    Epoch 26/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.2603 - acc: 0.9218     
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.2606 - acc: 0.9214 - val_loss: 358.0750 - val_acc: 0.3794
    
    Epoch 27/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.1260 - acc: 0.9647 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.1259 - acc: 0.9647 - val_loss: 1.3363 - val_acc: 0.7094
    
    Epoch 28/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0323 - acc: 0.9882 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0329 - acc: 0.9878 - val_loss: 0.0061 - val_acc: 0.9986
    
    Epoch 29/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0403 - acc: 0.9873 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0402 - acc: 0.9873 - val_loss: 1.3600 - val_acc: 0.7094
    
    Epoch 30/30
    2124/2126 [============================>.] - ETA: 0s - loss: 0.0235 - acc: 0.9911 
    2126/2126 [==============================] - 49s 23ms/sample - loss: 0.0235 - acc: 0.9911 - val_loss: 0.9180 - val_acc: 0.7884
    

    下面是输出的acc与loss变化图:
    acc.png
    loss.png

    得到的模型文件一览(每轮训练都保存):
    在这里插入图片描述
    分割线


    在模型训练这一步,我针对原先二分类时保存模型的问题做了改进。原先的博客只在训练完成后保存最终的模型,然而最后一个模型并不一定是最好的模型。在最开始的几次训练里,出现了这样的结果:
    在这里插入图片描述
    本来在第九轮训练出了一个val_acc=1模型,可是在第十轮的时候val_acc骤降到0.3794,然而我这时用的是之前博客的代码,所以只保存了最后的第十轮的模型,非常裂开!于是我查阅资料,找到了使用Callbacks函数,保存每一次训练的模型或者所有有提升的模型的方法。

    保存模型:
    Keras如何保存每轮(每个Epoch)训练后的模型?
    Keras回调函数Callbacks使用详解及训练过程可视化

    可以看到,在改变分类策略,改变数据集划分方法(增加划分随机性)后,训练的结果大大改善。有一段时间可以收敛得到到的acc与val_acc=1.0000的模型,也就是百分之百可以在训练集与测试集上成功分类!而且loss也非常低。我重复训练了多次,基本上每次训练都可以训练出acc达到1的模型。可以说,单看训练结果,这个模型的理论效果是极好的。

    但是由于数据集过小的问题,模型自然是出现了严重的过拟合,鲁棒性很差!经过我后续的实际测试,这个模型只能在数据集上进行正确度极高的分类,一旦采用其他照片,分类的效果就不尽人意。

    改进的方法也是老生常谈:增大数据集、进行数据增强。但是由于时间关系以及条件限制,我没有做进一步的改善。

    由于训练模型之后,显存与内存都不会自动清空(特别是显存,每次训练都被吃满6g),所以每次训练完都要重启spyder才能进行下一步的训练,比较不爽。所以我就查阅资料,找到了清空显存的办法。不过这个代码执行完后,gpu无法调用,还是需要重启,所以就算了。内存的清空似乎没有很干脆的方法(大概把读进内存的数据集变量del就行了?),而且我的内存足够跑三次训练,就跳过了。

    清理显存:
    python – Keras:完成训练后释放内存

    最后放一下我存放所有训练过的模型的文件夹:
    在这里插入图片描述
    接下来,进入调用模型进行检测的步骤。

    2.5.3 调用模型

    先给代码:

    检测模型_test3.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Thu Nov 12 22:12:38 2020
    @author: xyw
    """
    import numpy as np
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from tensorflow.keras.preprocessing import image
    
    model = tf.keras.models.load_model('C:\\Users\\Administrator\\Desktop\\test3\\classes=3_times=1_e30bs12\\myresnet50model_3classes_times=1_e30bs12_etimes=23_valacc=1.00.h5')
    #选择导入的模型
    
    # # test
    img_path = "C:\\Users\\Administrator\\Desktop\\vedio\\3\\338.jpg"
    img = image.load_img(img_path, target_size=(224, 224))
    
    
    plt.imshow(img)
    img = image.img_to_array(img)/ 255.0
    img = np.expand_dims(img, axis=0)  # 为batch添加第四维
    
    predict = model.predict(img)
    print(predict)    #预测标签
    predict=np.argmax(predict,axis=1)
    print(predict)    #预测类别
    

    输出的结果为:
    在这里插入图片描述
    我调用的模型是myresnet50model_3classes_times=1_e30bs12_etimes=23_valacc=1.00.h5,这是所有模型中loss最低,acc最高的一个模型。

    我选择的图片位于vidio\3\338,是一张困的图片,预测的标签为(0,0,1),预测的类别为2,结果正确。(状态与标签、类别的对应见2.5.1的定义)

    经过实测,模型在数据集范围内是可用的。

    接下来的工作就是将调用模型的代码打包,整合为一个可以实时预测驾驶员状态的代码文件了。

    2.6 成品——整合检测模型(三分类)

    先给代码,这个代码只是将数据集提取与调用模型整合了一下,改成了从摄像头定时拍照,并且加了一个循环用的函数来实现持续的检测。

    整合检测模型.py
    
    # -*- coding: utf-8 -*-
    """
    Created on Fri Nov 13 14:40:29 2020
    @author: xyw
    """
    
    '''1、引入函数'''
    import time
    import cv2
    import os
    import shutil
    import numpy as np
    import tensorflow as tf
    from tensorflow.keras.preprocessing import image
    
    '''2、制作数据处理模块函数,思路:获取图片缓存--逐张检测--清缓存'''
    def capimage(outPutDirName,img_num): #从摄像头提取图片的函数,两个参数分别是缓存文件夹路径、提取图片总数
        print('start')
        times=0
    #提取视频的频率,每1帧提取一个
        frameFrequency=1
    #输出图片到当前目录vedio文件夹下q
        if not os.path.exists(outPutDirName):
        #如果文件目录不存在则创建目录
            os.makedirs(outPutDirName)
        camera = cv2.VideoCapture(0) #调用摄像头
        while True:
            times+=1
            res, image = camera.read()
            if not res:
                print('not res , not image')
                break
            if times%frameFrequency==0 and times*0.1==int(times*0.1):
                cv2.imwrite(outPutDirName + str(int(times*0.1))+'.jpg', image)
                #每遍历10帧保存一张图片,可以修改times的倍率来修改提取的帧数间隔(提取速度)
                print(str(int(times*0.1)))
            if int(times*0.1)==int(img_num) : 
                break    #如果提取的图片数=预设需要提取的总图片数,则打破循环。
        print('图片提取结束,共'+img_num+'张')  
        camera.release()
    
    def delateimage(outPutDirName):    #用于清空缓存文件夹的函数
        shutil.rmtree(outPutDirName)
        os.makedirs(outPutDirName)
        print('已清空cache文件夹')
        
    '''前面是用摄像头采集图片缓存与清缓存,这个是测试函数'''    
    
    def test(cache_path,model_path):    #用于调用模型预测的函数,
        #两个参数是缓存文件夹路径(存有待预测的全部图片,与上面的文件夹是同一个)、调用模型文件的路径
        pre_list=list()    #用来保存预测结果的列表
        model = tf.keras.models.load_model(model_path)    #加载模型
        
        for img_name in os.listdir(cache_path):    #调用模型
            img_path = cache_path + img_name 
            img = image.load_img(img_path, target_size=(224, 224))
            img = image.img_to_array(img) / 255.0
            img = np.expand_dims(img, axis=0)  # 为batch添加第四维    
            predict = model.predict(img)
            print(predict)
            predict=np.argmax(predict,axis=1)
            print(predict)    #输出单张图片预测结果
            lable=int(predict)
            pre_list.append(lable)    #将结果加入预测列表
            
        print(pre_list)    #输出预测结果列表
    
    '''3、现在开始运行文件,确定三个参数'''
    img_num = '10' 
    ##修改这个参数来确定提取多少张图片-1
    cache_path='C:\\Users\\Administrator\\Desktop\\test3\\cache\\'  
    ##这是缓存文件夹的路径参数-2
    model_path = 'C:\\Users\\Administrator\\Desktop\\test3\\classes=3_times=1_e30bs12\\myresnet50model_3classes_times=1_e30bs12_etimes=23_valacc=1.00.h5'
    ##这是预测选用的权重文件的路径参数-3
    
    def loopMonitor():    #循环器函数,能够实现让内部的函数每隔一段时间运行一次的功能,即实现持续检测
        while True:
            capimage(cache_path,img_num)   #若是使用自带的已经全部是待检测图片的文件夹(自带检测集)而非摄像头,则不要运行这个,直接使用test函数即可
            test(cache_path,model_path)    #若是使用自带检测集,则cache改为文件夹路径
            delateimage(cache_path)        #若是使用自带检测集,则千万不要运行这个,否则会清空文件夹
            ##下面是定时循环器,里面的参数是等待秒数,用time.sleep函数可以每隔一段时间做一次检测,这样循环是为了能随时用ctrl+c打断运行
            print('wait...')
            for i in range(30):
                time.sleep(1)
    
    loopMonitor()                 ##运行循环器函数(内部打包了其他函数)
    

    我使用摄像头,对自己目前的状态进行判断,结果如下:

    start
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    图片提取结束,10[[1.8995142e-05 2.9743055e-04 9.9968362e-01]]
    [2]
    [[1.7572362e-06 9.3681127e-01 6.3186973e-02]]
    [1]
    [[1.0628138e-06 9.4999039e-01 5.0008602e-02]]
    [1]
    [[1.5084094e-06 9.2358166e-01 7.6416746e-02]]
    [1]
    [[6.7236647e-06 8.0073249e-01 1.9926082e-01]]
    [1]
    [[1.8759165e-06 9.3310726e-01 6.6890813e-02]]
    [1]
    [[1.3127151e-06 9.5798534e-01 4.2013396e-02]]
    [1]
    [[6.7462111e-07 9.6975416e-01 3.0245209e-02]]
    [1]
    [[2.6920061e-06 9.2666048e-01 7.3336884e-02]]
    [1]
    [[3.8068411e-06 8.9639020e-01 1.0360592e-01]]
    [1]
    [2, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    已清空cache文件夹
    wait...
    KeyboardInterrupt
    

    这个检测器是使用capimage函数抓取一些照片,并存在缓存文件夹中,然后利用test函数调用模型,对缓存文件夹中的照片进行预测并输出结果。在预测完之后会清空缓存文件夹,以便执行下一个循环。(为了方便调试,将照片写入硬盘的缓存文件夹中而非内存,这一点可以改进,避免在硬盘上进行过多擦写)

    按照loopMonitor的参数,每30秒会执行一次上述循环。我在控制台界面输入ctrl+c终止了该函数。这一点参考了以下网页:

    Python3-定时任务的四种实现方式

    利用这个函数,在实际运行时可以使这个检测器不断运行,实现每隔30s检测输出一次的功能。因为在time.sleep函数运行过程中时,只能等到时间结束,控制台才能用ctrl+c打断运行,所以使用了一个for循环方便打断。

    理论上,利用最后的结果列表pre_list,就可以判断目前摄像头拍到的照片所处的状态。但是实际上并不能做到成功的预测,原因前面解释过,是数据集的问题。另外的改进方法是使用人脸识别器,在数据集制作与检测时将人脸单独提取出来进行使用,应该能够提高精度。

    写到这里,我最主要的工作就介绍完毕了,开心!

    下面我将简单梳理一下深度学习方法进行疲劳检测的基本思路。

    2.7 深度学习方法小结

    2.7.1 文件结构

    文件结构

    2.7.2 整体思路

    原始视频 --> 数据预处理:opencv视频转图片.py --> 三种状态的图片 --> 数据集制作_三分类.py --> 数据集文件.npy --> resnet训练模型_三分类.py --> 可用的模型文件.h5 --> 进行实时疲劳检测:整合检测模型.py

    2.7.3 改进方向

    1、增大数据集,使用更多人、更多场景、更多角度、更多光线条件下的图片进行训练。

    2、使用人脸检测器,提取人脸照片来进行训练与检测,与普通的检测模型结合使用。(为了不丢失体态、低头信息)

    3、进行数据增强

    4、改进神经网络模型,可以使用其他的神经网络模型或微调模型。

    5、增加时间维度,结合动作检测,使用视频片段进行训练。这样能够保留动作与时序信息,效果应该更好,而且还能顺便检测其他危险驾驶行为。本来想尝试一下,不过查阅资料以后感觉能力不够,搞不出来,遂放弃。

    视频片段识别与行为检测的一些综述:
    驾驶员行为检测+疲劳驾驶
    人工智能竞赛-行为识别参赛总结
    视频的行为识别
    PA3D-基于姿态的3D视频行为识别网络

    三、dlib方法

    3.1 问题所在

    在完成了深度学习方法的疲劳检测后,我重新回到了dlib上。由于在编译上实在没什么办法,所以我想到了另一个途径——万能的淘宝。
    我在淘宝上咨询了一下,得到了这个结果:在这里插入图片描述
    我之前在安装dlib的时候也按照一些教程安装了vs2019,不过当时在里面安装的是python,没有安装c++。(查到的资料只提到要安装vs,没有更进一步的说明,我太菜了。。)看到这里之后,我去vs里又把c++安装了一下。然后我卸载用whl装的dlib库,重新用pip install安装。在经过漫长的等待后,居然成功了。。。
    当时人傻了,很无语,折腾了这么久,问题居然就这?
    在这里插入图片描述
    既然装好了dlib库,那么dlib方法也可以尝试一下了。

    3.2 dlib疲劳检测

    实测能用。

    dlib库检测人脸使用方法与简单的疲劳检测应用

    在这里插入图片描述
    应该可以用dlib库来提取人脸,实现上文说的,在采集数据时单独提取使用人脸照片,进行训练与检测。不过由于时间关系,在dlib方法上,我没有做进一步的尝试了。

    后记

    本次的探索性实验开始于大二上学期第六周周五,本来应该是搞三周的,但是我在课余时间陆陆续续搞了挺久。我在第八周周三开始决定尝试深度学习,在10月30日周五配好环境,在11月12日做出了最终的成品,在11月21日终于安好了dlib。在尝试的过程中,我遇到了很多困难,踩了很多坑,最终做出了一个理论上可行的成品,也算完成任务了吧。

    对我而言,这次的实验更大的意义是让我有了一个契机去学习一些课程之外的东西,而且我在不断学习尝试成功的过程中也挺开心,挺有成就感的,同时也打发了时间 ,这也是我在课余时间做下去的动力。

    由于这次实验的过程非常曲折,做的挺开心,放寒假也没事干,有时间,我就决定试着将所有细节与读过的内容都写进报告里,既是纪念,也方便以后翻看。因此这个实验报告写的很长,也有很多口语化、情绪化的表达。我可能会将主要的部分挑出来复制粘贴,写一份简洁一些的报告,可能也方便老师学长交差 。同时学期结束后我大概要换电脑了,到时候要重新安装环境,免不了一番折腾,所以我才在安装上写了比较长的篇幅(对我来说,安装的过程比正式工作的部分还要困难)。把代码与读过的内容保存一下,也方便以后参考。

    最后,衷心感谢黄玲老师提供了这次实验的机会,也衷心感谢洪佩鑫师兄在我遇到困难时无私的帮助我,希望大家新年快乐,来年一切顺利。

    完结撒花~~

    最后编辑于:2021.1.17 13:18

    展开全文
  • 提高型实验报告 实验课题 小屋 实验类型综合型 eq \o\ac)设计型 应用型 实验课程 三维角色与场景设计 专业名称 实验班级 实 验 者 实验时间 教育科学与技术学院 实验目的 1对所学的内容进行一个总结和梳理 2能灵活...
  • Java画板实验报告.doc

    2020-08-09 00:10:34
    实验目的图形界面设计 熟悉java.awt包中的组件掌握图形界面设计方法理解委托事件处理模型 2.实验要求 1设计图形界面添加菜单窗口上添加各种组件及菜单并处理组件及菜单的事件监听程序 2实现画板的基本功能 二程序...
  • MATLAB高数实验 主 题投资组合问题 指导教师阮小娥老师 制作时间一 学 院机械学院 小组成员机自07 赵磊 80 机自07 周策 81 机自07 邹业兵82 一引言 : 错误 ! 未定义书签 二实验问题 . 错误 ! 未定义书签 三问题分析 ...
  • 完全全自主制作的手机模型,原型为LG GW620+ 诺基亚N85后盖。基本满足期末实验报告等各种对源文件的要求。带有AVI演示爆炸效果。
  • U3D实验报告处理

    2019-12-19 19:42:31
    U3D中AnimationClip一般是在Model下的 作为模型的子对象 然后将这些动画片段(实际上也就是一秒50帧那种离散的参数的集合)附加到动画控制机上的某个状态上。 可以创建一个PlayerAC 也就是玩家的Animation...

    U3D中AnimationClip一般是在Model下的 作为模型的子对象

    然后将这些动画片段(实际上也就是一秒50帧那种离散的参数的集合)附加到动画控制机上的某个状态上。

    可以创建一个PlayerAC 也就是玩家的AnimationController 

     

    然后制作动画状态吧

    这些就是动画状态哈 你可以创建 命名 然后为这些动画状态添加AnimationClip 然后添加动画转换的条件(也就是那些箭头),我们可以在脚本里控制这些转换条件实现动画转换,当然了动画最终也是要一帧一个参数交给渲染系统才能有用,

     

    然后当然Player下面添加一个Animator组件 然后将PlayerAC拖到里面作为实体

    然后通过脚本控制动画状态的切换 

    你一个动画状态对应一段动画片段后 由变量控制在播放哪个动画片段,然后执行动画片段的时间线 ,运行到哪一帧将参数(就是蒙皮矩阵)以你玩那些游戏你会看到 你他们动画状态的切换是很生硬的瞬间完成的 

     

    、、、

    我想我知道 链接与直接嵌入本地视频的不同了,链接 只是给你一个地址,而本地视频是赋值一份视频格式与word封装到一块

    所以在别人电脑你在用那个视频超链接就不行了 为什么链接链接吧 为什么叫超呢 因为超文本 即可能是媒体文件的一个链接

    、、、

     

     

    展开全文
  • 然而,软件过程领域的最新研究结果表明,软件过程概念已不仅局限在软件开发和软件维护,而是发展到包括系统集成和软件产品的制作与生产。SEE旨在解决软件过程中各个过程和活动如何按照各条路径并行完成。管理、支持...
  • 第三模块:制作一个简单的表盘,画刻度 第四模块:获取当前时间,画秒钟时钟分钟 第五模块:隐藏秒钟分钟时钟 总体模型: 四、仓库代码链接 (https://gitee.com/hcy-yxdm123/HCY/blob/master...

    一、实验项目名称

    项目名称:实时时钟

    二、实验项目功能描述

    功能:时钟的走动。

    三、项目模块结构介绍(文字+总体模型)

    第一模块:初始化图形窗口
    第二模块:设计游戏尺寸
    第三模块:制作一个简单的表盘,画刻度
    第四模块:获取当前时间,画秒钟时钟分钟
    第五模块:隐藏秒钟分钟时钟
    总体模型:
    1581369-20190620152717301-1630695827.jpg

    四、仓库代码链接

    https://gitee.com/hcy-yxdm123/HCY/blob/master/%E6%97%B6%E9%92%9F.cpp

    五、实现界面展示(截图)

    1581369-20190620152755125-919142722.png

    六、实验总结(提出问题+解决办法+感想)

    问题:头文件问题,在Dev上一直运行错误;
    解决方法:问同学发现要按照书上的下载一个软件,Dev不可以运行。
    问题:不会使用visual studio这个软件;
    解决方法:查百度后只会在上面编写代码,不会如何改正代码。
    感想:感觉这一次的学习很费劲,不会使用Visual studio这个软件来运行代码,还有制作钟盘,实现针的动态走动。利用钟表的特点设计出我的时钟,要求精细。

    转载于:https://www.cnblogs.com/da123hcy/p/11058982.html

    展开全文
  • 实验报告结构是否清晰,问题叙述是否具有逻辑性 Web数据库应用程序设计的正确性 六 时间的安排 共3周(见教学办安排)。每个同学可以根据自身实际情况掌握进度,做完整个项目即可验收,最晚到实践课的最后一天。 ...
  • c++第一次实验

    2018-11-14 18:51:29
    c++的第一次上机实验。(使用c++语言制作“图书馆模型” 信控学院专业课实验报告实验报告
  • 四、实验报告要求 23 实验二、空间数据库管理及属性编辑 24 一、实验目的 24 二、实验准备 24 三、实验内容及步骤 25 第1步 启动ArcCatalog打开一个地理数据库 25 第2步 预览地理数据库中的要素类 26 第3步 创建缩图...
  • 实验五 树及二叉树 1.实验目的 掌握二叉树的建立与存储; 2)掌握二叉树的遍历方法; 2.实验要求 ...6.实验结果:上交所制作的文件和实验报告。 实验代码: #include<iostream> using namespac

    实验五 树及二叉树
    1.实验目的

    1. 掌握二叉树的建立与存储;
      2)掌握二叉树的遍历方法;
      2.实验要求
      要求能够熟练运用 DEV C++ 6.0软件所提供的菜单、工具、模型等编写二叉树的相关应用。
      3.实验内容
      使用 DEV C++ 6.0 来编写二叉树的中序遍历递归算法。
      4.实验步骤
    2. 先序遍历的顺序建立二叉链表;
    3. 中序遍历二叉树的递归算法;
      5.实验学时:2 学时。
      6.实验结果:上交所制作的文件和实验报告。
      实验代码:
    #include<iostream>
    using namespace std;
    #define OK 0
    #define Error -1
    static int i = 0;
    static char ch;
    
    typedef struct BiNode{
    	char data;
    	struct BiNode *Lchild,*Rchild;
    }BiTNode,*BiTree;
    
    int createBiTree(BiTree &T){
    	if(ch=='#')  {
    	T=NULL;return OK;
    	}
    	cin>>ch;
    	if(ch=='#')  {
    	T=NULL;	cout<<"建造完成,叶子结点共有"<<i<<"个"<<endl;return OK;
    	}
    	else{
    		cout<<"建造中...叶子结点现有"<<++i<<"个"<<endl;
    		
    		T=new BiTNode;
    		T->data=ch;
    		createBiTree(T->Lchild);
    		createBiTree(T->Rchild); 
    		//Sleep(10);
    	}
    } 
    
    void seeTree(BiTree T){
    	if(T)
    	{
    		seeTree(T->Lchild);
    		cout << T->data << " ";
    		seeTree(T->Rchild); 
    	}
    
    }
    
    int main()
    {
    	BiTree T;
    	cout<<"输入建立二叉列表序列,以#结尾:"<<endl;
    	createBiTree(T);
    	//cout<<T->Rchild->data; 
    	cout<<"中序遍历结果:"<<endl;
    	seeTree(T);
    	cout<<endl; 
    	return 0;
    } 
    

    运行结果:
    在这里插入图片描述

    7.实验小结。
    通过本次实验,我对二叉树和二叉链表的了解更加深入,掌握了对二叉树的先序、中序和后序的遍历。在程序编写过程中,我遇到了一些困难,如:DEV编译器不允许main函数为空;先序建立二叉树时,由于cin>>ch在if语句之前,所以每次递归都要先执行cin>>ch ,影响了函数正常结束,我将ch设为全局变量,并且在输入语句前也加入一个if语句来判断ch的值,使函数功能正常运行。

    展开全文
  • ArcGIS实验指导书 完整清晰版 下载

    热门讨论 2011-02-28 12:57:19
    四、实验报告要求 23 实验二、空间数据库管理及属性编辑 24 一、实验目的 24 二、实验准备 24 三、实验内容及步骤 25 第1步 启动ArcCatalog打开一个地理数据库 25 第2步 预览地理数据库中的要素类 26 第3步 创建缩图...
  • 实验报告分为:第一章引言 第二章模型车设计制作思路 第三章模型车机械设计说明 第四章控制电路设计说明 第五章控制软件设计说明 第六章开发工具、制作调试 第七章模型车主要技术参数
  • 这是一个实验性Javascript实用程序,用于组合MakeHuman模型的皮肤层。 所有皮肤图像均由MakeHuman项目制作并属于该项目,所有功劳应归功于它们。 错误报告,评论和投诉应在此处进行,而不应在MakeHuman项目中进行。 ...
  • 3.实验报告要求包括项目开发计划、需求规格说明书、设计规格说明书、源程序清单、测试报告和用户手册。全组文档格式、内容参照附件中模板,提交一份完整的实验报告(提交打印和电子两种形式)。 4.每组所选软件...
  • 实验考核成绩由小组成绩和个人成绩两部分构成,小组评分是从整体上对每组的课程设计报告进行评分,其目的是为了让学生体会到软件开发团队合作的重要性。个人成绩是对小组中成员在实验中的工作评价。 软件工程实验是...
  • 根据软件工程实施过程中的各阶段活动,我们可以把它归结为不同的软件生存期模型,并归结出每一阶段的实施的行为特征。在软件工程的实施过程中,需要制做相应的文档。 1.计划阶段 计划阶段指技术人员辅助管理人员或...
  • ArcGIS教程,GIS软件应用

    热门讨论 2010-06-22 16:03:10
    四、实验报告要求 23 实验二、空间数据库管理及属性编辑 24 一、实验目的 24 二、实验准备 24 三、实验内容及步骤 25 第1步 启动ArcCatalog打开一个地理数据库 25 第2步 预览地理数据库中的要素类 26 第3步 创建缩图...
  • 飞思卡尔智能车视频

    2018-02-04 18:32:22
    组委会提供一个标准的汽车模型、直流电机和可充电式电池,参赛队伍要制作一个能够自主识别路径的智能车,在专门设计的跑道上自动识别道路行驶,谁最快跑完全程而没有冲出跑道并且技术报告评分较高,谁就是获胜者。...
  • 我们在实验室规模的测试中优化了echo-PIV系统的设计,然后在水箱中使用安全壳模型制作了原型。 小型原型成功地定位了模拟泄漏并绘制了一块模拟碎片的表面。 该原型可以轻松扩大规模,以便在福岛第一核电站进行检查...
  • 心理学实验程序,收集一批被试数据并进行统计分析,最后呈交一份报告。 所选文献(第10篇):Aylward, J., Valton, V., Ahn, W. Y., Bond, R. L., Dayan, P., Roiser, J. P., & Robinson, O. J. (2019). Altered ...
  • 学员在中科院学习期间独立完成制作ARM开发板、开发触摸屏驱动等36个嵌入式专题实验项目,1-3个大型项目。其他实验项目如:智能机器人等可在结业后完成。 教学周期:10个月,其中第一学期3个月,第二学期5个月,课程...
  • 智能车开发板原理图

    2015-03-28 22:17:09
    组委会提供一个标准的汽车模型、直流电机和可充电式电池,参赛队伍要制作一个能够自主识别路径的智能车,在专门设计的跑道上自动识别道路行驶,最快跑完全程而没有冲出跑道并且技术报告评分较高为获胜者。...
  • 组委会提供一个标准的汽车模型、直流电机和可充电式电池,参赛队伍要制作一个能够自主识别路径的智能车,在专门设计的跑道上自动识别道路行驶,谁最快跑完全程而没有冲出跑道并且技术报告评分较高,谁就是获胜者。...
  • 常规的设计流程首先根据工业造型需要设计出结构,制作出油泥模型之后将其送到风洞实验室去测量空气动力学性能,然后再根据实验结果对模型进行反复修改直到获得满意结果为止,如此所得到的最终油泥模型才是符合需要的...
  • 电子设计.doc

    2019-05-28 11:58:28
    0067、同步电机模型的MATLAB仿真论文资料 0068、危险气体泄露报警器设计论文资料 0069、微型打印机控制电路的设计论文资料 0070、温度监控系统的设计论文资料 0071、温度控制系统设计论文资料 0072、无线调频发射器...

空空如也

空空如也

1 2
收藏数 39
精华内容 15
关键字:

模型制作实验报告