预防疲劳驾驶系统原来这么简单!从零使用人工智能教你制作

Keras,人脸识别,OpenCV和PIL的完整实战

在我们的生活中,因为疲劳驾驶而导致交通事故的案例举不胜举。对于每一个驾驶员来说开车的时候如果感觉到疲劳就需要立即找地方停下来,让身体得到适当的休息后再开车上路。

由于这种严重的错误,我着手开发一个神经网络,如果眼睛是闭着的,可以检测,而当与计算机视觉串联应用,以检测是否自己的眼睛关闭超过一秒钟。对于对提高驾驶安全性感兴趣的任何人,包括商业和日常驾驶员,汽车公司和车辆保险公司,这种技术都是有用的。

主要内容:

  1. 建立卷积神经网络
  2. 网络摄像头应用

数据采集

我们使用了来自多个来源的全脸数据,即来自UMass Amherst的睁眼数据和来自南京大学的闭眼数据。

然后,我们使用一个简单的python函数从该数据集中裁剪出眼睛,从而为我们提供了30,000多张裁剪后的眼睛图像。我们为每种图像裁剪添加了一个缓冲区,不仅可以吸引眼睛,还可以吸引眼睛周围的区域。稍后将在网络摄像头部分重新使用此裁剪功能。

# 从命令行安装
# brew install cmake
# pip install dlib
# pip install face_recognition
# imports:
from PIL import Image, ImageDraw
import face_recognition
import os
def eye_cropper(folders):
    # 建立计数以进行迭代文件保存
    count = 0

    # For循环遍历每个图像文件
    for folder in os.listdir(folders):
        for file in os.listdir(folders + '/' + folder):

            # 在图像上使用面部识别库
            image = face_recognition.load_image_file(folders + '/' + folder + '/' + file)
            # 为面部特征坐标创建变量
            face_landmarks_list = face_recognition.face_landmarks(image)

            # 为眼睛坐标创建一个占位符列表
            eyes = []
            try:
                eyes.append(face_landmarks_list[0]['left_eye'])
                eyes.append(face_landmarks_list[0]['right_eye'])
            except:
                continue
            # 建立眼睛的最大x和y坐标
            for eye in eyes:
                x_max = max([coordinate[0] for coordinate in eye])
                x_min = min([coordinate[0] for coordinate in eye])
                y_max = max([coordinate[1] for coordinate in eye])
                y_min = min([coordinate[1] for coordinate in eye])
              # 确定x和y坐标的范围
                x_range = x_max - x_min
                y_range = y_max - y_min

                #确保捕捉到全眼,
                #计算具有50%的正方形的坐标
                #在更大范围的轴上增加了缓冲
                if x_range > y_range:
                    right = round(.5*x_range) + x_max
                    left = x_min - round(.5*x_range)
                    bottom = round(((right-left) - y_range))/2 + y_max
                    top = y_min - round(((right-left) - y_range))/2
                else:
                    bottom = round(.5*y_range) + y_max
                    top = y_min - round(.5*y_range)
                    right = round(((bottom-top) - x_range))/2 + x_max
                    left = x_min - round(((bottom-top) - x_range))/2

                #使用缓冲坐标裁剪原始图像
                im = Image.open(folders + '/' + folder + '/' + file)
                im = im.crop((left, top, right, bottom))

                #调整图片大小以输入模型
                im = im.resize((80,80))

                # 将文件保存到输出文件夹
                im.save('yourfolderforcroppedeyes')

                # 增加迭代文件保存的数量
                count += 1
                # 每200张照片打印一次以监视进度
                if count % 200 == 0:
                    print(count)

# 调用功能以裁剪全脸眼睛图像
eye_cropper('yourfullfaceimagefolder')

警告:手动清理此数据集,除非你希望人们眨眼或让200张Bill Clinton的照片训练模型。这是我们用于训练模型的数据示例:

创建卷积神经网络

确定指标

因为预测积极的等级(睡眠的驾驶员)比预测负面的等级(清醒的驾驶员)对我们更重要,所以我们最重要的指标将是召回率(敏感度)。召回率越高,模型错误地预测处于唤醒状态的睡眠驱动程序(假阴性)越少。

这里唯一的问题是,我们的积极阶层远远超过我们的消极阶层。因此,最好使用F1分数或Precision-Recall AUC分数,因为它们还考虑了我们猜测驾驶员睡着了但真正醒着的时间(精度)。否则,我们的模型将始终预测我们处于睡眠状态并且无法使用。我们不用于处理不平衡图像数据的另一种方法是使用图像增强。

准备图像数据

下一步是导入图像并对模型进行预处理。

本节所需的导入:

import cv2
import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split
from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from keras.models import Sequential
from keras.layers import Dense,Flatten,Conv2D,MaxPooling2D,Dropout

导入我们之前创建的图像并调整图像的大小,以便它们都匹配。对于此项目,我们将尺寸调整为80x80像素。这是使用OS库的简单导入功能:

def load_images_from_folder(folder, eyes = 0):
    count = 0
    error_count = 0
    images = []
    for filename in os.listdir(folder):
        try:
            img = cv2.imread(os.path.join(folder,filename))
            img = cv2.resize(img, (80,80)) ## Resizing the images
            ## 表示眼睛是否为0:睁开; 1:闭眼
            images.append([img, eyes])
        except:
            error_count += 1
            print('ErrorCount = ' + str(error_count))
            continue

        count += 1
        if count % 500 == 0:
            print('Succesful Image Import Count = ' + str(count))

    return images

folder="../data/train/new_open_eyes"
open_eyes = load_images_from_folder(folder, 0)

folder="../data/train/New_Closed_Eyes"
closed_eyes = load_images_from_folder(folder, 1)
eyes = close_eyes + open_eyes

设置变量,其中独立的X是图像,而相关的y是相应的标签(闭眼为1,睁眼为0):

X = []
y = []
for features, label in eyes:
     X.append(features)
     y.append(label)

将图像转换为数组,以便可以输入模型。此外,将数据除以255即可缩放数据。

X = np.array(X).reshape(-1, 80, 80, 3)
y = np.array(y)
X = X/255.0

使用scikit Learn's将数据分为训练集和验证集train_test_split。重要提示:使 一定要分层y方法,因为我们已经不平衡类。

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = y)

创建模型架构

卷积层:

该层创建像素的子集而不是整个图像,并允许更快的模型。根据您设置的滤镜数量,其密度可能会比原始图像高或低,但是它们将使模型能够使用更少的资源来学习更复杂的关系。我使用了32个过滤器。使用至少一个卷积层,通常需要两个或多个。对我来说,最佳设置是将两个3x3池合并在一起,然后将三个3x3池合并在一起。CNN的总体趋势是使用更小的过滤器尺寸。实际上,双重3x3层与5x5层基本相同,但是速度更快,并且通常会获得更高的得分,正如Arnault Chazareix在这篇精彩文章中所解释的。之后的合并并非总是必要或更好。尝试使用模型,如果可能,选择是否使用。

展平

确保展平图像阵列,以使其可以进入密集层。

致密层

层越密集,模型训练所需的时间就越长。随着这些层中神经元数量的增加,网络学习到的关系复杂性将增加。通常,卷积层的想法是避免必须进行过深的密集层方案。对于我们的模型,我们使用了三层具有relu激活的层,神经元速率逐渐降低(256、128、64)。我们还在每一层之后使用了30%的辍学率

输出层

最后,由于这是一个二进制分类问题,请确保对您的外层使用S形激活。

编译模型

在中model.compile(),您需要将指标设置为PR AUC(tf.keras.metrics.AUC (curve = 'PR')在tensorflow中)或调用(tf.keras.metrics.recall在tensorflow中)。设置损失等于,binary_crossentropy因为通常这是一个二进制分类模型和一个好的优化程序adam。

拟合模型

通常将批次大小设置得尽可能大,但不要在此过程中炸毁机器!我在Google Colab的32 GB TPU上进行了网格搜索,并轻松地运行了1000多个批次。如有疑问,请尝试32个批次,如果不使内存过载,请增加批次。就时代而言,在20个时代之后收益递减,所以我不会比这个特定的CNN高很多。

这是Tensorflow Keras的完整设置:

#实例化模型
model = Sequential()

#添加前三个卷积层
model.add(Conv2D(
                filters = 32, #过滤器数量
                kernel_size = (3,3), #高度/过滤器的宽度
                activation = 'relu', #激活功能
                input_shape = (80,80,3) # 输入的图像形状
                ))
model.add(Conv2D(
                filters = 32, #过滤器数量
                kernel_size = (3,3), #高度/过滤器的宽度
                activation = 'relu' #激活功能
                ))
model.add(Conv2D(
                filters = 32, #过滤器数量
                kernel_size = (3,3), #高度/过滤器的宽度
                activation = 'relu' #激活功能
                ))

#在卷积层之后添加池
model.add(MaxPooling2D(pool_size = (2,2))) # 正在合并的区域的尺寸

#添加第二组卷积层
model.add(Conv2D(
                filters = 32, #过滤器数量
                kernel_size = (3,3), #高度/过滤器的宽度
                activation = 'relu' #激活功能
                ))
model.add(Conv2D(
                filters = 32, #过滤器数量
                kernel_size = (3,3), #高度/过滤器的宽度
                activation = 'relu' #激活功能
                ))

#添加最后一个池化层。
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Flatten())

#添加第一个具有256个节点的密集层
model.add(Dense(256, activation='relu'))

#添加一个辍学层以避免过度拟合
model.add(Dropout(0.3))

model.add(Dense(124, activation='relu'))
model.add(Dropout(0.3)) 

model.add(Dense(64, activation='relu'))
model.add(Dropout(0.3))

#添加输出层
model.add(Dense(1, activation = 'sigmoid'))

#编译模型
model.compile(loss='binary_crossentropy',
                optimizer='adam',
                metrics=[tf.keras.metrics.AUC(curve = 'PR')])

#拟合模型
model.fit(X_train,
            y_train,
            batch_size=800,
            validation_data=(X_test, y_test),
            epochs=24)

#评估模型
model.evaluate(X_test, y_test, verbose=1)

曲线分数下的最终精度召回区域:

0.981033

创建网络摄像头应用

拥有满意的模型后,请使用保存它model.save('yourmodelname.h5')。在保存生产模型时,请确保在没有验证数据的情况下运行生产模型。导入时,这会在旅途中引起问题。

安装和导入:

这些都是Mac优化的,尽管也可以在Windows上使用相同的脚本。

#网络摄像头应用程序所需的安装
install opencv-python
#如果要播放警报声音:
#pip install -U PyObjC
#pip install playsound
import cv2
from playsound import playsound
# import model saved above
eye_model = keras.models.load_model('best_model.h5’)

使用OpenCV访问网络摄像头

使用cv2.VideoCapture(0)启动摄像头捕获。如果您要根据相对帧大小而不是绝对坐标来确定文本位置,请确保使用来保存网络摄像头的宽度和高度cap.get(cv2.CAP_PROP_FRAME_WIDTH)。您还可以每秒查看帧数。可在此处找到OpenCV的捕获属性的完整列表。

cap = cv2.VideoCapture(0)
w = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
h = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print(cap.get(cv2.CAP_PROP_FPS))
if not cap.isOpened():
 raise IOError('Cannot open webcam’)

使用OpenCV捕获帧并裁剪它们

如果打算对被框框闭上的眼睛进行计数,请务必设置一个计数器。一个while True:循环将保持在相机上,直到你的脚本完成。在该while循环中,使用ret, frame = cap.read()格式捕获网络摄像头视频的帧。最后,在框架上调用该函数。它应该从帧中返回一个裁剪的眼睛,如果在帧中找不到眼睛,该函数将返回None无法被255除的眼睛,并跳至下一帧。

counter = 0
# 创建在使用网络摄像头时运行的while循环
while True:
  # 捕获网络摄像头输出的帧
  ret, frame = cap.read()
  # 帧上调用的函数
  image_for_prediction = eye_cropper(frame)
  try:
     image_for_prediction = image_for_prediction/255.0
  except:
     continue

通过模型运行框架

然后,我们可以通过模型运行图像并获得预测。如果预测值更接近于0,则我们在屏幕上显示“打开”。否则(即接近1),我们显示“已关闭”。请注意,如果模型检测到睁开的眼睛,计数器将重置为0,如果睁开的眼睛将计数器增加1。我们可以使用显示一些基本文字来指示眼睛是闭合还是睁开cv2.putText()。

prediction = eye_model.predict(image_for_prediction)
if prediction < 0.5:
  counter = 0
  status = 'Open’
  cv2.putText(frame, status, (round(w/2)-80,70),
cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 2, cv2.LINE_4)

else:
  counter = counter + 1
  status = 'Closed’
  cv2.putText(frame, status, (round(w/2)-104,70), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2, cv2.LINE_4)

如果连续6帧闭着眼睛(“睡觉”),我们也要显示警报。可以使用简单的if语句完成此操作:

if counter > 5:
  cv2.putText(frame, 'DRIVER SLEEPING’, (round(w/2)-136,round(h) — 146), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2, cv2.LINE_4)
  counter = 5

最后,我们需要显示框架并为while循环提供退出键。在cv2.waitKey(1)确定多长时间的帧将被显示。括号中的数字是显示帧的毫秒数,除非按下“ k”键(在这种情况下为27)或退出键:

cv2.imshow('Drowsiness Detection’, frame)
k = cv2.waitKey(1)
if k == 27:
  break

在循环之外,释放网络摄像头并关闭应用程序:

cap.release()
cv2.destroyAllWindows()

最终产品

有了一些风格上的补充,这就是最终的产品。

如您所见,该模型非常有效,尽管训练时间很长,但在几毫秒内即可返回预测。经过一些进一步的改进并导出到外部计算机,该程序可以很容易地在实际情况下应用,甚至可以挽救生命。

原创:Dustin Stewart

链接:
https://towardsdatascience.com/drowsiness-detection-using-convolutional-neural-networks-face-recognition-and-tensorflow-56cdfc8315ad

(0)

相关推荐

  • 基于OpenCV的图像卡通化

    重磅干货,第一时间送达 本期将创建一个类似于Adobe Lightroom的Web应用程序,使用OpenCV和Streamlit实现图像的卡通化 作为一个狂热的街头摄影爱好者,几乎每个周末都要在城市中 ...

  • 熬了几个大夜,学完一套985博士总结的「卷积神经网络、目标检测、OpenCV」学习笔记(20G高清/...

    AI 显然是最近几年非常火的一个新技术方向,从几年前大家认识到 AI 的能力,到现在产业里已经在普遍的探讨 AI 如何落地了. 我们可以预言未来在很多的领域,很多的行业,AI 都会在里边起到重要的作用 ...

  • Python+OpenCV 十几行代码模仿世界名画

    现在很多人都喜欢拍照(自拍).有限的滤镜和装饰玩多了也会腻,所以就有 APP 提供了模仿名画风格的功能,比如 prisma.versa 等,可以把你的照片变成 梵高.毕加索.蒙克 等大师的风格. 这种 ...

  • 使用OpenCV对运动员的姿势进行检测

    重磅干货,第一时间送达 如今,体育运动的热潮日益流行.同样,以不正确的方式进行运动的风险也在增加.有时可能会导致严重的伤害.考虑到这些原因,提出一种以分析运动员的关节运动,来帮助运动员纠正姿势的解决方 ...

  • 基于OpenCV和Tensorflow的深蹲检测器

    重磅干货,第一时间送达 本期我们将介绍如和使用OpenCV以及Tensorflow实现深蹲检测 在检疫期间,我们的体育活动非常有限,这样并不好.在进行一些居家运动时,我们必须时刻保持高度的注意力集中, ...

  • 使用Python中的OpenCV降噪功能增强图像的3个步骤

    重磅干货,第一时间送达 在本文中,我们将展示如何通过三个简单的步骤来实现降噪.我们将使用机器学习训练的降噪模型.这是我们找到的最好的降噪模型之一. 程序可以判断图像是否有噪点吗?这对于另一个项目可能是 ...

  • 实战:使用 PyTorch 和 OpenCV 实现实时目标检测系统

    重磅干货,第一时间送达 一.引言 自动驾驶汽车可能仍然难以理解人类和垃圾桶之间的区别,但这并没有使最先进的物体检测模型在过去十年中取得的惊人进步相去甚远. 将其与 OpenCV 等库的图像处理能力相结 ...

  • 【从零学习OpenCV 4】深度神经网络应用实例

    重磅干货,第一时间送达 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍<OpenCV 4开发详解>.为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通 ...

  • 【从零学习OpenCV 4】颜色模型与转换

    重磅干货,第一时间送达 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍<从零学习OpenCV 4>.为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通 ...

  • opencv调用darknet

    本文主要介绍如何通过opencv调用已经训练好的darknet模型进行目标检测 1.模型及配置文件下载 需要下载以下文件 已经训练好的模型权重文件 **.weights 模型配置文件 yolov3.c ...

  • Python Opencv捕获视频

    先得安装这个opcv的库 注意选择的Python解释器 我这里还有一个解释器,是thonny的IDE的 装好以后可以看看版本 会发现是可以自动补全的 我们可以在本地的时候放一个图片,读取一下 impo ...

  • 利用OpenCV实现基于深度学习的超分辨率处理

    重磅干货,第一时间送达 OpenCV是一个非常强大的计算机视觉处理的工具库.很多小伙伴在入门图像处理时都需要学习OpenCV的使用.但是随着计算机视觉技术的发展,越来越多的算法涌现出来,人们逐渐觉得O ...

  • opencv调用yolov3模型来进行图像检测

    之前使用了opencv来调用ssd的模型来检测物体,今天学了一下用opencv调用yolov3的模型来检测物体,二者在预测图形的部分,代码流程差不多,反正就是加载模型然后预测输出,但是对于输出结果的处 ...