OpenCV进阶篇01

第14章 视频处理

OpenCV不仅能够处理图像,还能够处理视频。视频是由大量的图像构成的,这些图像以固定的时间间隔从视频中获取。这样,就能够使用图像处理的方法对这些图像进行处理,进而达到处理视频的目的。要处理视频,需要先对视频进行读取、显示和保存等相关操作。为此,OpenCV提供了VideoCapture类和VideoWriter类的相关方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flqLumsh-1639056600119)(OpenCV进阶篇01.assets/image-20211128195439971.png)]

14.1 读取并显示摄像头视频

摄像头视频指的是从摄像头(见图14.1)中实时读取到的视频。为了读取并显示摄像头视频,OpenCV提供了VideoCapture类的相关方法,这些方法包括摄像头的初始化方法、检验摄像头初始化是否成功的方法、从摄像头中读取帧的方法和关闭摄像头的方法等。下面依次对这些方法进行讲解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FhdOlx82-1639056600120)(OpenCV进阶篇01.assets/image-20211128195512490.png)]

​ 图14.1 摄像头说明
视频是由大量的图像构成的,把这些图像称作帧。

14.1.1 VideoCapture类

VideoCapture类提供了构造方法VideoCapture(),用于完成摄像头的初始化工作。VideoCapture()的语法格式如下:

 capture = cv2.VideoCapture(index)

参数说明:

capture:要打开的摄像头。

index:摄像头的设备索引。

注意
摄像头的数量及其设备索引的先后顺序由操作系统决定,因为OpenCV没有提供查询摄像头的数量及其设备索引的任何方法。
当index的值为0时,表示要打开的是第1个摄像头;对于64位的Windows 10笔记本,当index的值为0时,表示要打开的是笔记本内置摄像头,关键代码如下:

 capture = cv2.VideoCapture(0)

当index的值为1时,表示要打开的是第2个摄像头;对于64位的Windows 10笔记本,当index的值为1时,表示要打开的是一个连接笔记本的外置摄像头,关键代码如下:

 capture = cv2.VideoCapture(1)

为了检验摄像头初始化是否成功,VideoCapture类提供了isOpened()方法。isOpened()方法的语法格式如下:

 retval = cv2.VideoCapture.isOpened()

参数说明:

retval:isOpened()方法的返回值。如果摄像头初始化成功,retval的值为True;否则,retval的值为False。

说明
在VideoCapture()的语法格式基础上,isOpened()方法的语法格式可以简写为retval = capture.isOpened()
摄像头初始化后,可以从摄像头中读取帧,为此VideoCapture类提供了read()方法。read()方法的语法格式如下:

 retval, image = cv2.VideoCapture.read() # 可以简写为retval, image = capture.read()

参数说明:

retval:是否读取到帧。如果读取到帧,retval的值为True;否则,retval的值为False。

image:读取到的帧。因为帧指的是构成视频的图像,所以可以把“读取到的帧”理解为“读取到的图像”。

OpenCV官网特别强调,在不需要摄像头时,要关闭摄像头。为此,VideoCapture类提供了release()方法。release()方法的语法格式如下:

 cv2.VideoCapture.release() # 可以简写为capture.release()

14.1.2 如何使用VideoCapture类

在14.1.1节中,介绍了VideoCapture类中的VideoCapture()方法、isOpened()方法、read()方法和release()方法。那么,在程序开发的过程中,如何使用这些方法呢?本节将通过3个实例进行讲解。

【实例14.1】 读取并显示摄像头视频。
编写一个程序,打开笔记本内置摄像头实时读取并显示视频。当按下空格键时,关闭笔记本内置摄像头,销毁显示摄像头视频的窗口,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zu4htrrv-1639056600121)(OpenCV进阶篇01.assets/image-20211128200517716.png)]

上述代码的运行结果如图14.2所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S3x2StrA-1639056600122)(OpenCV进阶篇01.assets/image-20211128200557699.png)]

​ 图14.2 读取并显示摄像头视频说明
图14.2是笔者用笔记本内置摄像头实时读取并显示公司天花板的视频。
在实例14.1运行期间,如果按下空格键,笔记本内置摄像头将被关闭,显示摄像头视频的窗口也将被销毁。此外,PyCharm控制台将输出如图14.3所示的警告信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Et4TKT9V-1639056600123)(OpenCV进阶篇01.assets/image-20211128200630928.png)]

​ 图14.3 PyCharm控制台输出的警告信息
为了消除图14.3所示的警告信息,需要将实例14.1第3行代码:

 capture = cv2.VideoCapture(0) # 打开笔记本内置摄像头

修改为如下代码:

 capture = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 打开笔记本内置摄像头

如果想打开的是一个连接笔记本的外置摄像头,那么需要将实例14.1第3行代码:

capture = cv2.VideoCapture(0) # 打开笔记本内置摄像头
修改为如下代码:

 capture = cv2.VideoCapture(1, cv2.CAP_DSHOW) # 打开笔记本外置摄像头

实例14.1已经成功地读取并显示了摄像头视频,那么如何对这个视频进行处理呢?其实,处理视频所用的方法与处理图像所用的方法是相同的。实例14.2将使用处理图像的相关方法把实例14.1读取并显示的彩色视频转换为灰度视频。

【实例14.2】 将摄像头视频由彩色视频转换为灰度视频。
编写一个程序,使用图像处理的相关方法把实例14.1读取并显示的彩色视频转换为灰度视频。当按下空格键时,关闭笔记本内置摄像头,销毁显示摄像头视频的窗口,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZbc21i3-1639056600123)(OpenCV进阶篇01.assets/image-20211128200727172.png)]

上述代码的运行结果如图14.4所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zNghqOZ-1639056600124)(OpenCV进阶篇01.assets/image-20211128200757312.png)]

                                                                           图14.4 把彩色视频转换为灰度视频

实例14.1和实例14.2都用到了按键指令。当按下空格键时,关闭笔记本内置摄像头,销毁显示摄像头视频的窗口。那么,能否通过按键指令,保存并显示摄像头视频某一时刻的图像?带着这个疑问,请读者朋友继续阅读实例14.3。

【实例14.3】 显示并保存摄像头视频某一时刻的图像。
编写一个程序,打开笔记本内置摄像头实时读取并显示视频。当按下空格键时,关闭笔记本内置摄像头,保存并显示此时摄像头视频中的图像,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWCR3A9A-1639056600124)(OpenCV进阶篇01.assets/image-20211128200847208.png)]

上述代码的运行结果如图14.5所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOQinG4E-1639056600124)(OpenCV进阶篇01.assets/image-20211128200934953.png)]

​ 图14.5 显示摄像头视频某一时刻的图像
实例14.3除能够显示摄像头视频某一时刻的图像外(见图14.5),还能够把图14.5保存为D盘根目录下的copy.png文件,如图14.6所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHynPLNJ-1639056600125)(OpenCV进阶篇01.assets/image-20211128201000098.png)]

​ 图14.6 把图14.5保存为D盘根目录下的copy.png
实例14.1~实例14.3打开的都是笔记本内置摄像头,如果在打开笔记本内置摄像头的同时,再打开一个连接笔记本的外置摄像头,应该如何实现呢?

【实例14.4】 读取并显示2个摄像头视频。

编写一个程序,在打开笔记本内置摄像头实时读取并显示视频的同时,再打开一个连接笔记本的外置摄像头。当按下空格键时,关闭笔记本内置摄像头和连接笔记本的外置摄像头,销毁显示摄像头视频的窗口。代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CsYWK3qB-1639056600125)(OpenCV进阶篇01.assets/image-20211128201055804.png)]

上述代码的运行结果如图14.7和图14.8所示。其中,图14.7是读取并显示笔记本内置摄像头视频,图14.8是读取并显示连接笔记本的外置摄像头视频。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8rny1w1T-1639056600126)(OpenCV进阶篇01.assets/image-20211128201118623.png)]

​ 图14.7 读取并显示笔记本内置摄像头视频

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ut2593Nl-1639056600126)(OpenCV进阶篇01.assets/image-20211128201143279.png)]

​ 图14.8 读取并显示连接笔记本的外置摄像头视频

14.2 播放视频文件

VideoCapture类及其方法除了能够读取并显示摄像头视频外,还能够读取并显示视频文件。当窗口根据视频文件的时长显示视频文件时,便实现了播放视频文件的效果。

14.2.1 读取并显示视频文件

VideoCapture类的构造方法VideoCapture()不仅能够完成摄像头的初始化工作,还能够完成视频文件的初始化工作。当VideoCapture()用于初始化视频文件时,其语法格式如下:

video = cv2.VideoCapture(filename)
参数说明:

video:要打开的视频。

filename:打开视频的文件名。例如,公司宣传.avi等。

注意
OpenCV中的VideoCapture类虽然支持各种格式的视频文件,但是这个类在不同的操作系统中,支持的视频文件格式不同。尽管如此,VideoCapture类能够在不同的操作系统中支持后缀名为.avi的视频文件。

【实例14.5】 读取并显示视频文件。
编写一个程序,读取并显示PyCharm当前项目路径下名为“公司宣传.avi”的视频文件。当按Esc键时,关闭视频文件并销毁显示视频文件的窗口,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0RujO8DJ-1639056600126)(OpenCV进阶篇01.assets/image-20211128201246445.png)]

上述代码的运行结果如图14.9所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8xA2soV1-1639056600131)(OpenCV进阶篇01.assets/image-20211128201314133.png)]

                                                        图14.9 读取并显示名为“公司宣传.avi”的视频文件说明

调整waitKey()方法中的参数值可以控制视频文件的播播放速度。例如,当代码为cv2.waitKey(1)时,等待用户按下键盘的时间为1ms,视频文件的播放速度非常快;当代码为cv2.waitKey(50)时,等待用户按下键盘的时间为50ms,能够减缓视频文件的播放速度。
使用处理图像的相关方法,能够将摄像头视频由彩色视频转换为灰度视频。那么,使用相同的方法,也能够将视频文件由彩色视频转换为灰度视频。

【实例14.6】 将视频文件由彩色视频转换为灰度视频。
编写一个程序,使用处理图像的相关方法,先将PyCharm当前项目路径下名为“公司宣传.avi”的视频文件由彩色视频转换为灰度视频,再显示转换后的灰度图像,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7OtfFzc2-1639056600132)(OpenCV进阶篇01.assets/image-20211128201410162.png)]

上述代码的运行结果如图14.10所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rOwHke9Q-1639056600132)(OpenCV进阶篇01.assets/image-20211128201434739.png)]

​ 图14.10 将“公司宣传.avi”由彩色视频转换为灰度视频

14.2.2 视频的暂停播放和继续播放

实例14.5使用VideoCapture类及其相关方法实现了在窗口中播放视频文件的效果。那么,能否在实例14.5的基础上,通过按键指令,在播放视频的过程中,实现视频的暂停播放和继续播放呢?答案是肯定的。

【实例14.7】 视频的暂停播放和继续播放。
编写一个程序,读取并显示PyCharm当前项目路径下名为“公司宣传.avi”的视频文件。在播放视频文件的过程中,当按空格键时,暂停播放视频;当再次按空格键时,继续播放视频;当按Esc键时,关闭视频文件并销毁显示视频文件的窗口,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dculEzq5-1639056600132)(OpenCV进阶篇01.assets/image-20211128201543614.png)]

上述代码的运行结果如图14.11和图14.12所示(其中,图14.11是暂停播放视频的效果,图14.12是继续播放视频的效果)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Xw26rsV-1639056600133)(OpenCV进阶篇01.assets/image-20211128202037480.png)]

​ 图14.11 暂停播放视频

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7YIJzv5-1639056600133)(OpenCV进阶篇01.assets/image-20211128202102795.png)]

​ 图14.12 继续播放视频

14.2.3 获取视频文件的属性

在实际开发中,有时需要获取视频文件的属性。为此,VideoCapture类提供了get()方法。get()方法的语法格式如下:

 retval = cv2.VideoCapture.get(propId)

参数说明:

retval:获取与propId对应的属性值。

propId:视频文件的属性值。

VideoCapture类提供视频文件的属性值及其含义如表14.1所示。

​ 表14.1 视频文件的属性值及其含义

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F3Tt5xP9-1639056600133)(OpenCV进阶篇01.assets/image-20211128202208827.png)]

说明
(1)视频是由大量的、连续的图像构成的,把其中的每一幅图像称作一帧。
(2)帧数指的是视频文件中含有的图像总数,帧数越多,视频播放时越流畅。
(3)在播放视频的过程中,把每秒显示图像的数量称作帧速率(FPS,单位:帧/s)。
(4)帧宽度指的是图像在水平方向上含有的像素总数。
(5)帧高度指的是图像在垂直方向上含有的像素总数。

【实例14.8】 获取并输出视频文件的指定属性值。
编写一个程序,使用VideoCapture类get()方法,先获取“公司宣传.avi”的帧速率、帧数、帧宽度和帧高度,再把上述4个属性值输出在PyCharm的控制台上,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMVvRcmt-1639056600134)(OpenCV进阶篇01.assets/image-20211128202243529.png)]

上述代码的运行结果如图14.13所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZRWvaFvb-1639056600134)(OpenCV进阶篇01.assets/image-20211128202327955.png)]

​ 图14.13 获取并输出“公司宣传.avi”的帧速率、帧数、帧宽度和帧高度
实例14.8演示了初始化视频文件后,获取并输出视频文件的指定属性值。那么,能否使得窗口在播放视频的同时,动态显示当前视频文件的属性值呢?例如,当前视频播放到第几帧,该帧对应着视频的第几秒等。

【实例14.9】 动态显示视频文件的属性值。
编写一个程序,窗口在播放“公司宣传.avi”视频文件的同时,动态显示当前视频播放到第几帧和该帧对应视频的第几秒,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WrTj9MW-1639056600134)(OpenCV进阶篇01.assets/image-20211128202356380.png)]

上述代码的运行结果如图14.14所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZcquZ4m-1639056600134)(OpenCV进阶篇01.assets/image-20211128202452972.png)]

​ 图14.14 动态显示视频文件的属性值

说明
图14.14中的185和7.4s的含义是当前视频播放到第185帧,第185帧对应着“公司宣传.avi”视频文件中的第7.4s。

14.3 保存视频文件

在实际开发过程中,很多时候希望保存一段视频。为此,OpenCV提供了VideoWriter类。下面先来熟悉一下VideoWriter类中的常用方法。

14.3.1 VideoWriter类

VideoWriter类中的常用方法包括VideoWriter类的构造方法、write()方法和release()方法。其中,VideoWriter类的构造方法用于创建VideoWriter类对象,其语法格式如下:

 <VideoWriter object> = cv2.VideoWriter(filename, fourcc, fps, frameSize)

参数说明:

VideoWriter object:VideoWriter类对象。

filename:保存视频时的路径(含有文件名)。

fourcc:用4个字符表示的视频编码格式。

fps:帧速率。

frameSize:每一帧的大小。
在OpenCV中,使用cv2.VideoWriter_fourcc()来确定视频编码格式。表14.2列出了几个常用的视频编码格式。

​ 表14.2 常用的视频编码格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D1BjmSHG-1639056600135)(OpenCV进阶篇01.assets/image-20211128202614177.png)]

根据上述内容,即可创建一个VideoWriter类对象。

例如,在Windows操作系统下,fourcc的值为cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’, ‘D’),帧速率为20,帧大小为640×480。如果想把一段视频保存为当前项目路径下的output.avi,那么就要创建一个VideoWriter类对象output,关键代码如下:

 fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')output = cv2.VideoWriter("output.avi", fourcc, 20, (640, 480))

上述代码也可以写作:

 fourcc = cv2.VideoWriter_fourcc(* 'XVID')output = cv2.VideoWriter("output.avi", fourcc, 20, (640, 480))

为了保存一段视频,除需要使用VideoWriter类的构造方法外,还需要使用VideoWriter类提供的write()方法。write()方法的作用是在创建好的VideoWriter类对象中写入读取到的帧,其语法格式如下:

 cv2.VideoWriter.write(frame)

参数说明:

frame:读取到的帧。

注意
使用write()方法时,需要由VideoWriter类对象进行调用。例如,在创建好的VideoWriter类对象output中写入读取到的帧frame,关键代码如下:

 output.write(frame)

当不需要使用VideoWriter类对象时,需要将其释放掉。为此,VideoWriter类提供了release()方法,其语法格式如下:

cv2.VideoWriter.release()
例如,完成保存一段视频后,需要释放VideoWriter类对象output。关键代码如下:

 output.release()

14.3.2 如何使用VideoWriter类

使用VideoWriter类保存一段视频需要经过以下几个步骤:创建VideoWriter类对象、写入读取到的帧、释放VideoWriter类对象等。而且,这段视频既可以是摄像头视频,也可以是视频文件。本节将使用VideoWriter类以实例的方式分别对保存摄像头视频和保存视频文件进行讲解。

【实例14.10】 保存一段摄像头视频。
编写一个程序,首先打开笔记本内置摄像头,实时读取并显示视频;然后按Esc键,关闭笔记本内置摄像头,销毁显示摄像头视频的窗口,并且把从打开摄像头到关闭摄像头的这段视频保存为PyCharm当前项目路径下的output.avi,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TpyezTBT-1639056600135)(OpenCV进阶篇01.assets/image-20211128202745833.png)]

说明
在Windows操作系统下,fourcc的值为cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’, ‘D’),帧速率为20,帧大小为640×480。
在上述代码运行的过程中,按Esc键后,会在PyCharm当前项目路径(D:\PyCharm\PythonDevelop)下生成一个名为“output.avi”的视频文件,如图14.15所示。双击打开D:

PyCharm\PythonDevelop路径下的“output.avi”视频文件,即可浏览被保存的摄像头视频,如图14.16所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYij0Tio-1639056600135)(OpenCV进阶篇01.assets/image-20211128202856720.png)]

​ 图14.15 PyCharm当前项目路径下的output.avi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tTjVHUdj-1639056600135)(OpenCV进阶篇01.assets/image-20211128202918741.png)]

​ 图14.16 浏览被保存的摄像头视频说明

这里是使用笔记本内置摄像头录制的手机秒表的视频,读者可以根据自己的喜好录制其他视频。
实例14.10可以重复运行,由于output.avi已经存在于PyCharm当前项目路径下,因此新生成的output.avi会覆盖已经存在的output.avi。
从图14.16中能够发现,笔者使用笔记本内置摄像头录制的视频时长为26s。也就是说,从打开摄像头、到关闭摄像头的这段时间间隔为26s,并且这段时间间隔由是否按Esc键决定。那么,能否对这段时间间隔进行设置呢?例如,打开摄像头并显示10s的摄像头视频?如果能,又该如何编写具有如此功能的代码呢?

【实例14.11】 保存一段时长为10s的摄像头视频。
编写一个程序,首先打开笔记本内置摄像头,实时读取并显示视频;然后录制一段时长为10s的摄像头视频;10s后,自动关闭笔记本内置摄像头,同时销毁显示摄像头视频的窗口,并且把这段时长为10s的摄像头视频保存为PyCharm当前项目路径下的ten_Seconds.avi,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7GFATdq-1639056600136)(OpenCV进阶篇01.assets/image-20211128203038547.png)]

运行上述代码10s后,会在PyCharm当前项目路径下生成一个名为“ten_Seconds.avi”的视频文件。双击打开D:\PyCharm\PythonDevelop路径下的“ten_Seconds.avi”视频文件,即可浏览被保存的摄像头视频,如图14.17所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pt90Msfs-1639056600136)(OpenCV进阶篇01.assets/image-20211128203057475.png)]

​ 图14.17 浏览被保存的、时长为10s的摄像头视频

实例14.10和实例14.11演示了如何使用VideoWriter类保存摄像头视频。VideoWriter类不仅能保存摄像头视频,还能保存视频文件,而且保存视频文件与保存摄像头视频的步骤是相同的。接下来,仍以实例的方式演示如何使用VideoWriter类保存视频文件。

【实例14.12】 保存视频文件。
编写一个程序,首先读取PyCharm当前项目路径下名为“公司宣传.avi”的视频文件,然后将“公司宣传.avi”视频文件保存为PyCharm当前项目路径下的copy.avi,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCaAwZTh-1639056600136)(OpenCV进阶篇01.assets/image-20211128203126554.png)]

由于要以帧为单位,一边读取视频文件,一边保存视频文件,因此运行上述代码后,PyCharm控制台没有立即输出代码中的提示信息,如图14.18所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cdqLRfA-1639056600136)(OpenCV进阶篇01.assets/image-20211128203221855.png)]

​ 图14.18 PyCharm控制台没有立即输出代码中的提示信息
大约1min后,会在PyCharm当前项目路径下生成一个名为“copy.avi”的视频文件,如图14.19所示。这时,PyCharm控制台也将输出如图14.20所示的提示信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U2lJ4ljQ-1639056600137)(OpenCV进阶篇01.assets/image-20211128203239999.png)]

​ 图14.19 PyCharm当前项目路径下生成的copy.avi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JDHW90we-1639056600137)(OpenCV进阶篇01.assets/image-20211128203302454.png)]

​ 图14.20 PyCharm控制台将输出提示信息
双击打开D:\PyCharm\PythonDevelop路径下的“copy.avi”视频文件,即可浏览被保存的视频文件,如图14.21所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cly0iSLz-1639056600137)(OpenCV进阶篇01.assets/image-20211128203324502.png)]

​ 图14.21 浏览被保存的“copy.avi”视频文件

从图14.21中能够发现,保存后的“copy.avi”视频文件的时长为49s。那么,能否缩短“copy.avi”视频文件的时长?例如,只保存“公司宣传.avi”视频文件中的前10s视频?这是可以实现的,实现逻辑与实例14.11是相同的。

【实例14.13】 保存视频文件中的前10s视频。
编写一个程序,首先读取PyCharm当前项目路径下名为“公司宣传.avi”的视频文件,然后将“公司宣传.avi”视频文件中的前10s视频保存为PyCharm当前项目路径下的ten_Seconds.avi,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUcxf5F9-1639056600138)(OpenCV进阶篇01.assets/image-20211128203418911.png)]

运行上述代码10s后,不仅会在PyCharm当前项目路径下生成一个名为“ten_Seconds.avi”视频文件,而且会在PyCharm控制台输出提示信息。双击打开D:\PyCharm\PythonDevelop路径下的“ten_Seconds.avi”视频文件,即可浏览被保存的视频文件,如图14.22所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8coQjsKg-1639056600138)(OpenCV进阶篇01.assets/image-20211128203447064.png)]

​ 图14.22 保存“公司宣传.avi”视频文件中的前10s视频

14.4 小结

视频是由一系列连续的图像构成的,这一系列连续的图像被称作帧,帧是以固定的时间间隔从视频中获取的。因为视频播放的速度就是获取帧的速度,所以把视频播放的速度称作帧速率,其单位是帧/s(即1s内出现的图像数)。所谓视频处理,处理的对象就是从视频中获取的帧,而后使用图像处理的方法对获取的帧进行处理。OpenCV提供了VideoCapture类和VideoWriter类处理视频,虽然这2个类在不同的操作系统中支持的视频文件的格式不同,但是这2个类在不同的操作系统中都支持AVI格式的视频文件。

第15章 人脸检测和人脸识别

人脸识别是基于人的脸部特征信息进行身份识别的一种生物识别技术,也是计算机视觉重点发展的技术。机器学习算法诞生之后,计算机可以通过摄像头等输入设备自动分析图像中包含的内容信息,随着技术的不断发展,现在已经有了多种人脸识别的算法。本章将介绍OpenCV自带的多种图像跟踪技术和3种人脸识别技术的用法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61xijGpt-1639056600138)(OpenCV进阶篇01.assets/image-20211128203604892.png)]

15.1 人脸检测

人脸检测是让计算机在一幅画面中找出人脸的位置。毕竟计算机还达不到人类的智能水平,所以计算机在检测人脸的过程中实际上是在做“分类”操作,例如,计算机发现图像中有一些像素组成了眼睛的特征,那这些像素就有可能是“眼睛”;如果“眼睛”旁边还有“鼻子”和“另一只眼睛”的特征,那这3个元素所在的区域就很有可能是人脸区域;但如果“眼睛”旁边缺少必要的“鼻子”和“另一只眼睛”,那就认为这些像素并没有组成人脸,它们不是人脸图像的一部分。
检测人脸的算法比较复杂,但OpenCV已经将这些算法封装好,本节将介绍如何利用OpenCV自带的功能进行人脸检测。

15.1.1 级联分类器

将一系列简单的分类器按照一定顺序级联到一起就构成了级联分类器,使用级联分类器的程序可以通过一系列简单的判断来对样本进行识别。例如,依次满足“有6条腿”“有翅膀”“有头、胸、腹”这3个条件的样本就可以被初步判断为昆虫,但如果任何一个条件不满足,则不会被认为是昆虫。

OpenCV提供了一些已经训练好的级联分类器,这些级联分类器以XML文件的方式保存在以下路径中:

 ...\Python\Lib\site-packages\cv2\data\

路径说明: “…\Python\”:Python虚拟机的本地目录。 “\Lib\site-packages\”:pip安装扩展包的默认目录。 “\cv2\data\”:OpenCV库的data文件夹。
例如,这里的Python虚拟机安装在C:\Program Files\Python\目录下,级联分类器文件所在的位置如图15.1所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TpJmm6jg-1639056600138)(OpenCV进阶篇01.assets/image-20211128203651374.png)]

​ 图15.1 OpenCV自带的级联分类器XML文件
不同版本的OpenCV自带的级联分类器XML文件可能会有差别,data文件夹中缺少的XML文件可以到OpenCV的源码托管平台下载,地址为:https://github.com/opencv/opencv/tree/master/data/haarcascades。
每一个XML文件都对应一种级联分类器,但有些级联分类器的功能是类似的(正面人脸识别分类器就有3个),表15.1是部分XML文件对应的功能,

​ 表15.1 部分级联分类器XML的功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYP1IBib-1639056600139)(OpenCV进阶篇01.assets/image-20211128203757228.png)]

想要实现哪种图像检测,就要在程序启动时加载对应的级联分类器。下一节将介绍如何加载并使用这些XML文件。

15.1.2 方法

OpenCV实现人脸检测需要做两步操作:加载级联分类器和使用分类器识别图像。这两步操作都有对应的方法。
首先是加载级联分类器,OpenCV通过CascadeClassifier()方法创建了分类器对象,其语法如下:

 <CascadeClassifier object> = cv2.CascadeClassifier(filename)

参数说明:

filename:级联分类器的XML文件名。

返回值说明:

object:分类器对象。
然后使用已经创建好的分类器对图像进行识别,这个过程需要调用分类器对象的detectMultiScale()方法,其语法如下:

 objects = cascade.detectMultiScale(image, scaleFactor, minNeighbors, flags, minSize, maxSize)

对象说明:

cascade:已有的分类器对象。
参数说明:

image:待分析的图像

scaleFactor:可选参数,扫描图像时的缩放比例。

minNeighbors:可选参数,每个候选区域至少保留多少个检测结果才可以判定为人脸。该值越大,分析的误差越小。

flags:可选参数,旧版本OpenCV的参数,建议使用默认值。

minSize:可选参数,最小的目标尺寸。

maxSize:可选参数,最大的目标尺寸。

返回值说明:

objects:捕捉到的目标区域数组,数组中每一个元素都是一个目标区域,每一个目标区域都包含4个值,分别是:左上角点横坐标、左上角点纵坐标、区域宽、区域高。object的格式为:[[244 203 111 111] [432 81 133 133]]。
下一节将介绍如何在程序中使用这2个方法。

15.1.3 分析人脸位置

haarcascade_frontalface_default.xml是检测正面人脸的级联分类器文件,加载该文件就可以创建出追踪正面人脸的分类器,调用分类器对象的detectMultiScale()方法,得到的objects结果就是分析得出的人脸区域的坐标、宽和高。下面通过一个实例介绍如何实现此功能。【实例15.1】 在图像的人脸位置绘制红框。
将haarcascade_frontalface_default.xml文件放到项目根目录下的cascades文件夹中,加载此级联分类器之后,检测出所有可能是人脸的区域,通过for循环在这些区域上绘制红色边框,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZfLofZT-1639056600139)(OpenCV进阶篇01.assets/image-20211128203902576.png)]

上述代码的运行结果如图15.2所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WJgxYeFT-1639056600139)(OpenCV进阶篇01.assets/image-20211128203949284.png)]

​ 图15.2 检测出的人脸位置

【实例15.2】 戴墨镜特效。
手机拍照软件自带各种各样的贴图特效,实际上这些贴图特效就是先定位了人脸位置,然后在人脸相应位置覆盖素材实现的。OpenCV也可以实现此类功能,例如为人脸添加戴墨镜的特效,需要执行以下3个步骤:

(1)编写一个覆盖图片的overlay_img()方法。因为素材中可能包含透明像素,这些透明像素不可以遮挡人脸,所以在覆盖背景图像时要做判断,忽略所有透明像素。判断一个像素是否为透明像素,只需将图像从3通道转为4通道,判断第4通道的alpha值,alpha值为1表示完全不透明,0表示完全透明。
(2)创建人脸识别级联分类器,分析图像中人脸的区域。
(3)把墨镜图像按照人脸宽度进行缩放,并覆盖到人脸区域约1/3的位置。
实现以上功能的具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fi4CEBd2-1639056600139)(OpenCV进阶篇01.assets/image-20211128204015841.png)]

上述代码的运行效果如图15.3所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJSiaiWc-1639056600140)(OpenCV进阶篇01.assets/image-20211128204055080.png)]

​ 图15.3 戴墨镜特效

15.2 检测其他内容

OpenCV提供的级联分类器除了可以识别人脸以外,还可以识别一些其他具有明显特征的物体,如眼睛、行人等。本节将介绍几个OpenCV自带的级联分类器的用法。

15.2.1 眼睛检测

haarcascade_eye.xml是检测眼睛的级联分类器文件,加载该文件就可以追踪眼睛的分类器,下面通过一个实例来介绍如何实现此功能。【实例15.3】 在图像的眼睛位置绘制红框。
将haarcascade_eye.xml文件放到项目根目录下的cascades文件夹中,加载此级联分类器之后,检测出所有可能是眼睛的区域,通过for循环在这些区域上绘制红色边框,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gfP6GF7b-1639056600140)(OpenCV进阶篇01.assets/image-20211128204131328.png)]

上述代码的运行结果如图15.4所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PSlrFOlX-1639056600140)(OpenCV进阶篇01.assets/image-20211128204151205.png)]

​ 图15.4 检测出的眼睛位置

15.2.2 猫脸检测

OpenCV还提供了2个训练好的检测猫脸的级联分类器,分别是haarcascade_frontalcatface.xml和haarcascade_frontalcatface_extended.xml,前者的判断标准比较高,较为精确,但可能有些猫脸识别不出来;后者的判断标准比较低,只要类似猫脸就会被认为是猫脸。使用猫脸分类器不仅可以判断猫脸的位置,还可以识别图像中有几只猫。
下面通过一个实例来介绍如何实现此功能。

【实例15.4】 在图像里找到猫脸的位置。
为了得到比较理想的检测结果,建议使用haarcascade_frontalcatface_extended.xml。将haarcascade_frontalcatface_extended.xml文件放到项目根目录下的cascades文件夹中,加载此级联分类器之后,检测出所有可能是猫脸的区域,通过for循环在这些区域上绘制红色边框,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p4Q1akwE-1639056600141)(OpenCV进阶篇01.assets/image-20211128204247078.png)]

上述代码的运行结果如图15.5所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA7THiMr-1639056600141)(OpenCV进阶篇01.assets/image-20211128204308682.png)]

​ 图15.5 检测出猫脸的位置

15.2.3 行人检测

haarcascade_fullbody.xml是检测人体(正面直立全身或背面直立全身)的级联分类器文件,加载该文件就可以追踪人体的分类器,下面通过一个实例介绍如何实现此功能。

【实例15.5】 在图像中找到行人的位置。

将haarcascade_fullbody.xml文件放到项目根目录下的cascades文件夹中,加载此级联分类器之后,检测出所有可能是人形的区域,通过for循环在这些区域上绘制红色边框,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Kbv4N0m-1639056600141)(OpenCV进阶篇01.assets/image-20211128204421178.png)]

上述代码的运行结果如图15.6所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkjW6lM9-1639056600141)(OpenCV进阶篇01.assets/image-20211128204440594.png)]

​ 图15.6 检测出的行人位置

15.2.4 车牌检测

haarcascade_russian_plate_number.xml是检测汽车车牌的级联分类器文件,加载该文件就可以追踪图像中的车牌,下面通过一个实例来介绍如何实现此功能。

【实例15.6】 标记图像中车牌的位置。
将haarcascade_russian_plate_number.xml文件放到项目根目录下的cascades文件夹中,加载此级联分类器之后,检测出所有可能是车牌的区域,通过for循环在这些区域上绘制红色边框,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJT0G7zG-1639056600142)(OpenCV进阶篇01.assets/image-20211128204518734.png)]

上述代码的运行结果如图15.7所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3AGDpozV-1639056600142)(OpenCV进阶篇01.assets/image-20211128204601073.png)]

​ 图15.7 检测出的车牌位置

15.3 人脸识别

OpenCV提供了3种人脸识别方法,分别是Eigenfaces、Fisherfaces和LBPH。这3种方法都是通过对比样本的特征最终实现人脸识别。因为这3种算法提取特征的方式不一样,侧重点不同,所以不能分出孰优孰劣,只能说每种方法都有各自的识别风格。
OpenCV为每一种人脸识别方法都提供了创建识别器、训练识别器和识别3种方法,这3种方法的语法非常相似。本节将简单介绍如何使用这些方法。

15.3.1 Eigenfaces人脸识别器

Eigenfaces也叫作“特征脸”。Eigenfaces通过PCA(主成分分析)方法将人脸数据转换到另外一个空间维度做相似性计算。在计算过程中,算法可以忽略一些无关紧要的数据,仅识别一些具有代表性的特征数据,最后根据这些特征识别人脸。
开发者需要通过以下3种方法完成人脸识别操作。
(1)通过cv2.face.EigenFaceRecognizer_create()方法创建Eigenfaces人脸识别器,其语法如下:

 recognizer = cv2.face.EigenFaceRecognizer_create(num_components, threshold)

参数说明:

num_components:可选参数,PCA方法中保留的分量个数,建议使用默认值。

threshold:可选参数,人脸识别时使用的阈值,建议使用默认值。

返回值说明:

recognizer:创建的Eigenfaces人脸识别器对象。

(2)创建识别器对象后,需要通过对象的train()方法训练识别器。建议每个人都给出2幅以上的人脸图像作为训练样本。train()方法的语法如下:

 recognizer.train(src, labels)

对象说明:

recognizer:已有的Eigenfaces人脸识别器对象。
参数说明:

src:用来训练的人脸图像样本列表,格式为list。样本图像必须宽、高一致。

labels:样本对应的标签,格式为数组,元素类型为整数。数组长度必须与样本列表长度相同。样本与标签按照插入顺序一一对应。
(3)训练识别器后可以通过识别器的predict()方法识别人脸,该方法对比样本的特征,给出最相近的结果和评分,其语法如下:

 label, confidence = recognizer.predict(src)

对象说明:

recognizer:已有的Eigenfaces人脸识别器对象。
参数说明:

src:需要识别的人脸图像,该图像宽、高必须与样本一致。
返回值说明:

label:与样本匹配程度最高的标签值。

confidence:匹配程度最高的信用度评分。评分小于5000匹配程度较高,0分表示2幅图像完全一样。
下面通过一个实例来演示Eigenfaces人脸识别器的用法。

【实例15.7】 使用Eigenfaces识别人脸。
现以两个人的照片作为训练样本,第一个人的照片如图15.8~图15.10所示,第二个人的照片如图15.11~图15.13所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iECdDaVw-1639056600142)(OpenCV进阶篇01.assets/image-20211128204741974.png)]

​ 图15.8 Summer 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxw1keLJ-1639056600142)(OpenCV进阶篇01.assets/image-20211128204805362.png)]

​ 图15.9 Summer 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEzTNsvY-1639056600144)(OpenCV进阶篇01.assets/image-20211128204824476.png)]

​ 图15.10 Summer 3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHdp9ObP-1639056600145)(OpenCV进阶篇01.assets/image-20211128204847564.png)]

​ 图15.11 Elvis 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoAsLaYC-1639056600145)(OpenCV进阶篇01.assets/image-20211128204943018.png)]

​ 图15.12 Elvis 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFfkWZkZ-1639056600145)(OpenCV进阶篇01.assets/image-20211128205000900.png)]

​ 图15.13 Elvis 3
待识别的照片如图15.14所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A3At0MQV-1639056600145)(OpenCV进阶篇01.assets/image-20211128205024760.png)]

​ 图15.14 待识别照片
创建Eigenfaces人脸识别器对象,训练以上样本后,判断图15.13所示是哪一个人,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ebySwVQ2-1639056600146)(OpenCV进阶篇01.assets/image-20211128205102240.png)]

上述代码的运行结果如下:

 confidence = 18669.728291380223Summer

程序对比样本特征分析得出,被识别的人物特征最接近的是Summer。

15.3.2 Fisherfaces人脸识别器

Fisherfaces是由Ronald Fisher最早提出的,这也是Fisherfaces名字的由来。Fisherfaces通过LDA(线性判别分析技术)方法将人脸数据转换到另外一个空间维度做投影计算,最后根据不同人脸数据的投影距离判断其相似度。
开发者需要通过以下3种方法完成人脸识别操作。
(1)通过cv2.face.FisherFaceRecognizer_create()方法创建Fisherfaces人脸识别器,其语法如下:

 recognizer = cv2.face.FisherFaceRecognizer_create(num_components, threshold)

参数说明:

num_components:可选参数,通过Fisherface方法进行判断分析时保留的分量个数,建议使用默认值。

threshold:可选参数,人脸识别时使用的阈值,建议使用默认值。

返回值说明:

recognizer:创建的Fisherfaces人脸识别器对象。
(2)创建识别器对象后,需通过对象的train()方法训练识别器。建议每个人都给出2幅以上的人脸图像作为训练样本。train()方法的语法如下:

 recognizer.train(src, labels)

对象说明:

recognizer:已有的Fisherfaces人脸识别器对象。

参数说明:

src:用来训练的人脸图像样本列表,格式为list。样本图像必须宽、高一致。

labels:样本对应的标签,格式为数组,元素类型为整数。数组长度必须与样本列表长度相同。样本与标签按照插入顺序一一对应。

(3)训练识别器后可以通过识别器的predict()方法识别人脸,该方法对比样本的特征,给出最相近的结果和评分,其语法如下:

 label, confidence = recognizer.predict(src)

对象说明:

recognizer:已有的Fisherfaces人脸识别器对象。

参数说明:

src:需要识别的人脸图像,该图像宽、高必须与样本一致。
返回值说明:

label:与样本匹配程度最高的标签值。

confidence:匹配程度最高的信用度评分。评分小于5000程度较高,0分表示2幅图像完全一样。

下面通过一个实例演示Fisherfaces人脸识别器的用法。

【实例15.8】 使用Fisherfaces识别人脸。
现以2个人的照片作为训练样本,第一个人的照片如图15.15~图15.17所示,第二个人的照片如图15.18~图15.20所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MqOOL3ZG-1639056600146)(OpenCV进阶篇01.assets/image-20211128205304341.png)]

​ 图15.15 Mike 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fD0p7MfX-1639056600146)(OpenCV进阶篇01.assets/image-20211128205346389.png)]

​ 图15.16 Mike 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z68Kgd8h-1639056600146)(OpenCV进阶篇01.assets/image-20211128205403358.png)]

​ 图15.17 Mike 3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ks2JfSMO-1639056600147)(OpenCV进阶篇01.assets/image-20211128205425662.png)]

​ 图15.18 KaiKai 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yU9rs4qz-1639056600147)(OpenCV进阶篇01.assets/image-20211128205446503.png)]

​ 图15.19 KaiKai 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKhOVEVm-1639056600147)(OpenCV进阶篇01.assets/image-20211128205509193.png)]

​ 图15.20 KaiKai 3
待识别的照片如图15.21所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5lfyJm0-1639056600147)(OpenCV进阶篇01.assets/image-20211128205529188.png)]

​ 图15.21 待识别照片

创建Fisherfaces人脸识别器对象,训练以上样本后,判断图15.21是哪一个人,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2K5HBRcj-1639056600148)(OpenCV进阶篇01.assets/image-20211128205614870.png)]

上述代码的运行结果如下:

 confidence = 2327.170867892041Mike

程序对比样本特征分析得出,被识别的人物特征最接近的是KaiKai。

15.3.3 Local Binary Pattern Histogram人脸识别器

Local Binary Pattern Histogram简称LBPH,即局部二进制模式直方图,这是一种基于局部二进制模式算法,这种算法善于捕获局部纹理特征。
开发者需要通过以下3种方法来完成人脸识别操作。

(1)通过cv2.face. LBPHFaceRecognizer_create()方法创建LBPH人脸识别器,其语法如下:

 recognizer = cv2.face.LBPHFaceRecognizer_create(radius, neighbors, grid_x, grid_y, threshold)

参数说明:

radius:可选参数,圆形局部二进制模式的半径,建议使用默认值。

neighbors:可选参数,圆形局部二进制模式的采样点数目,建议使用默认值。

返回值说明:

grid_x:可选参数,水平方向上的单元格数,建议使用默认值。

grid_y:可选参数,垂直方向上的单元格数,建议使用默认值。

threshold:可选参数,人脸识别时使用的阈值,建议使用默认值。

(2)创建识别器对象后,需要通过对象的train()方法训练识别器。建议每个人都给出2幅以上的人脸图像作为训练样本。train()方法的语法如下:

 recognizer.train(src, labels)

对象说明:

recognizer:已有的LBPH人脸识别器对象。

参数说明:

src:用来训练的人脸图像样本列表,格式为list。样本图像必须宽、高一致。

labels:样本对应的标签,格式为数组,元素类型为整数。数组长度必须与样本列表长度相同。样本与标签按照插入顺序一一对应。

(3)训练识别器后就可以通过识别器的predict()方法识别人脸,该方法对比样本的特征,给出最相近的结果和评分,其语法如下:

 label, confidence = recognizer.predict(src)

对象说明:

recognizer:已有的LBPH人脸识别器对象。

参数说明:

src:需要识别的人脸图像,该图像宽、高必须与样本一致。

返回值说明:

label:与样本匹配程度最高的标签值。

confidence:匹配程度最高的信用度评分。评分小于50匹配程度较高,0分表示2幅图像完全一样。
下面通过一个实例来演示LBPH人脸识别器的用法。

【实例15.9】 使用LBPH识别人脸。
现以2个人的照片作为训练样本,第一个人的照片如图15.22~图15.24所示,第二个人的照片如图15.25~图15.27所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1IaYb3Z-1639056600148)(OpenCV进阶篇01.assets/image-20211128205807097.png)]

​ 图15.22 lxe 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z07uWpmp-1639056600148)(OpenCV进阶篇01.assets/image-20211128205827725.png)]

​ 图15.23 lxe 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ut9gAr3g-1639056600148)(OpenCV进阶篇01.assets/image-20211128205853204.png)]

​ 图15.24 lxe 3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGZIVUbG-1639056600149)(OpenCV进阶篇01.assets/image-20211128205910674.png)]

​ 图15.25 RuiRui 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRf0oFFj-1639056600149)(OpenCV进阶篇01.assets/image-20211128205956685.png)]

​ 图15.26 RuiRui 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-auO4l6Tq-1639056600149)(OpenCV进阶篇01.assets/image-20211128210013982.png)]

​ 图15.27 RuiRui 3
待识别的照片如图15.28所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DvKB4tRS-1639056600149)(OpenCV进阶篇01.assets/image-20211128210039659.png)]

​ 图15.28 待识别照片
创建LBPH人脸识别器对象,训练以上样本之后,判断图15.27是哪一个人,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lkJPhXra-1639056600150)(OpenCV进阶篇01.assets/image-20211128210105190.png)]

上述代码的运行结果如下:

 confidence = 45.082326535640014RuiRui

程序对比样本特征分析得出,被识别的人物特征最接近的是RuiRui。

15.4 小结

人脸检测和人脸识别是相辅相成的,这是因为在进行人脸识别前,要先判断当前图像内是否出现了人脸,这个判断过程需要由人脸检测完成。只有在当前图像内检测到人脸,才能判断出这张人脸属于哪个人,这个判断是由人脸识别器完成的。因此,人脸识别指的是程序先在图像内检测人脸,再识别这张人脸属于哪个人的过程。本章讲解了3种人脸识别器,读者要熟练掌握这3种人脸识别器的实现方法和实现原理。

第16章 MR智能视频打卡系统

很多公司都使用打卡机或打卡软件进行考勤。传统的打卡方式包括点名、签字、刷卡和指纹等。随着技术的不断发展,计算机视觉技术越来越强大,已经可以实现人脸打卡功能。打卡软件通过摄像头扫描人脸特征,利用人脸的差异识别人员。人脸打卡的准确性不输于指纹打卡,甚至安全性和便捷性都高于指纹打卡。本章将介绍一个由Python OpenCV开发的智能视频打卡系统。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AF8PpIXz-1639056600150)(OpenCV进阶篇01.assets/image-20211128210226814.png)]

16.1 需求分析

打卡系统有3个核心功能:录入打卡人的资料、员工打卡和查看打卡记录,在满足核心功能的基础上需要完善一些附加功能和功能细节。在开发MR智能视频打卡系统前,先对本系统的一些需求进行如下拆解和分析。

1.数据模型

本系统不使用第三方数据库,所有数据都以文本的形式保存在文件中,因此要规范数据内容和格式,建立统一模型。
若把软件的使用者设定为“公司”,那么打卡者身份可设定为“员工”,程序中数据模型就应该是员工数据类。
每一位员工都有姓名,“姓名”就作为员工类中必备的数据之一。
因为员工可能会重名,所以必须使用另一种标记作为员工身份的认证,即为每一位员工添加不重复的员工编号。员工编号的格式为从1开始递增数字,每添加一位新员工,员工编号就+1。员工类中添加“员工编号”。
系统中必须保存所有员工的照片用于人脸识别。为了区分每位员工的照片文件,程序使用“员工特征码+随机值.png”的规则为照片文件命名。如果使用员工编号作为特征码,1号员工和11号员工的文件名容易发生混淆,所以特征码不能使用员工编号,而是一种长度一致、复杂性高、不重复的字符串。员工类中添加“特征码”。
员工与编号、姓名、特征码是一对一的关系,但员工与打卡记录是一对多的关系,所以打卡记录可以放在员工类中保存,而不是单独保存在打卡记录模型中。打卡记录需要记录每一位员工的具体打卡时间,并能以报表的形式体现。可以使用字段保存打卡记录模型,员工姓名作为key,该员工的打卡记录列表作为value。

2.打卡功能

人脸打卡依赖于人脸识别功能。本程序可以使用OpenCV提供的人脸识别器实现此功能,建议使用正确率较高的LBPH识别器,其他识别器也可以考虑,但需要做好测试验证。
系统通过拍照保存员工的照片样本。当员工面对摄像头时,按Enter键就可以生成一张正面特写照片文件。为了增加识别准确率,每位员工应拍3张照片,也就是按3次Enter键才能完成录入操作。
OpenCV提供的人脸识别器有一个缺陷:必须比对2种不同样本才能进行判断。如果公司第一次使用打卡系统,系统中没有录入任何员工,缺少比对样本,OpenCV提供的人脸识别器就会报错。因此本系统应该给出几个无人脸的默认样本,保证即使只录入一个员工,该员工也能顺利打卡。

每次员工打卡成功后,都应该记录该员工的打卡时间,然后保存到文件中。

3.数据维护

数据维护总结起来就是增、删、改、查4种操作。简化版的打卡系统可以忽略“改”的操作,由先删除,再新增的方式代替。
本系统除了提供录入新员工的功能之外,也提供删除已有员工的功能。删除员工之前应输入验证码进行验证,以防用户操作失误,误删重要数据。确认执行删除操作后,不仅要删除员工的信息,也要同时删除员工的打卡记录和照片文件。完成删除操作后,所有数据文件中不再存有被删员工的任何数据。

4.考勤报表

每个公司的考勤制度都不同,很多公司都主动设置s“上班时间”和“下班时间”来做考勤的标准。员工要在“上班时间”之前打卡才算正常到岗,在“下班时间”之后打卡才算正常离岗。未在规定时间内打卡的情况属于“打卡异常”,“打卡异常”通常分为3种情况:迟到、早退或缺席(或缺勤)。
本系统分析每一位员工在某一天的打卡记录,如果该员工在“上班时间”前和“下班时间”后都有打卡记录,则认为该员工当天全勤,该员工当天的其他打卡记录会被忽略。但如果该员工在“上班时间”前未能打卡,而是在“上班时间”后到中午12点前打卡,这种情况被视为迟到。如果该员工在“下班时间”后未能打卡,而是在中午12点之后到“下班时间”前打卡,这种情况被视为早退。当天没有打卡记录被视为缺席。

16.2 系统设计

16.2.1 开发环境

本系统开发使用的环境如下:
Python版本:3.8.2
OpenCV版本:4.2.0
numpy版本:1.18.1

IED:PyCharm 2019.3.3 (Community Edition)
操作系统:Windows 7/Windows 10

16.2.2 功能结构

MR智能视频打卡系统的功能结构如图16.1所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9pV9M7R-1639056600150)(OpenCV进阶篇01.assets/image-20211128210408201.png)]

​ 图16.1 功能结构

16.2.3 业务流程

MR智能视频打卡系统的总体业务流程如图16.2所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6BybXRQJ-1639056600150)(OpenCV进阶篇01.assets/image-20211128210435530.png)]

​ 图16.2 总体业务流程
打卡功能业务流程如图16.3所示。
查看记录功能业务流程如图16.4所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JF5XYgoi-1639056600151)(OpenCV进阶篇01.assets/image-20211128210456043.png)]

​ 图16.3 打卡功能的业务流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MOSll7F-1639056600151)(OpenCV进阶篇01.assets/image-20211128210539359.png)]

​ 图16.4 查看记录功能的业务流程
员工管理功能业务流程如图16.5所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sCO8tmjW-1639056600151)(OpenCV进阶篇01.assets/image-20211128210604201.png)]

​ 图16.5 员工管理功能的业务流程
考勤报表功能业务流程如图16.6所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vPysFV37-1639056600152)(OpenCV进阶篇01.assets/image-20211128210648098.png)]

​ 图16.6 考勤报表功能的业务流程
员工管理、查看记录和考勤报表这3个功能中都涉及权限管理业务。如果用户要使用这3个功能,需要登录管理员账号,只有登录成功后才有权使用。权限管理业务流程如图16.7所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xiYgrT7E-1639056600152)(OpenCV进阶篇01.assets/image-20211128210720411.png)]

​ 图16.7 权限管理业务流程

16.2.4 项目结构

MR智能视频打卡系统的项目结构如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VQbSsmUZ-1639056600152)(OpenCV进阶篇01.assets/image-20211128210741943.png)]

16.3 文件系统设计

本程序没有使用任何数据库保存数据,而是采用直接读写文件的方式来保存数据。项目中的所有数据文件都保存在data文件夹中。
程序使用的数据文件及文件夹信息如表16.1所示。

​ 表16.1 程序使用的数据文件及文件夹信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MIQ2aYAG-1639056600152)(OpenCV进阶篇01.assets/image-20211128210902635.png)]

下面详细介绍每种数据文件的内容格式。
(1)employee_data.txt文件以字符串的形式保存所有员工的数据,数据之间用英文逗号隔开,一行保存一个员工。其格式如下:

 编号1,姓名1,特征码1编号2,姓名2,特征码2...

例如,employee_data.txt文件保存的实际内容可能如下:

 1,张三,5263802,李四,5710963,王五,381609

(2)lock_record.txt文件以字符串的形式保存数据,数据格式为打卡记录字典的字符串内容,其格式如下:

 {姓名a: [日期list], 姓名b: [日期list], ... , 姓名n:[日期list]}

例如,lock_record.txt文件保存的实际内容可能如下:

{‘张三’: [‘2020-04-15 14:59:54’], ‘李四’: [‘2020-04-15 15:02:08’], ‘王五’: [‘2020-04-15 15:11:02’, ‘2020-04-15
15:35:49’]}
(3)work_time.txt文件以字符串的形式保存数据,其格式如下:

 08:00:00/16:00:00

前一个时间为上班时间,后一个时间为下班时间,格式均为%H:%M:%S。系统以这2个时间为标准判断员工是否出现迟到、早退。
(4)user_password.txt文件以字符串的形式保存数据,数据格式为管理员账号密码字典的字符串内容,其格式如下:

 {管理员账号:管理员密码}

例如,user_password.txt文件保存的实际内容可能如下:

 {'mr': 'mrsoft', '123456': '123456'}

用户可以在这个文件中手动修改管理员账号和密码。
(5)/data/face/文件夹下保存的是所有员工的照片文件,格式为PNG。每张照片的大小都是640×480。每名员工需保存3张照片。
该文件夹下还有2个默认的图像文件,文件名分别为1000000000.png和2000000000.png。这是2幅纯色图像,用于辅助训练人脸识别器。
人脸识别器使用样本进行训练时,至少要有2个以上的标签分类。如果程序中仅保存了一位员工的照片,人脸识别器无法拿此员工照片与其他样本做对比,人脸识别器就会报错,此时2幅默认图像文件就充当了对比样本,以防止人脸识别器无法完成训练。当程序录入了足够多的员工信息后,这2幅默认图像虽然丧失了功能,但也不会影响识别器的识别能力。

16.4 数据实体模块设计

entity包下的organizations.py文件用于封装数据模型。该文件中设计了员工类,并提供一些维护数据的方法。接下来将详细介绍organizations.py中的代码。

1.构建员工类
创建Employee类作为员工类,并创建包含3个参数的构造方法。3个参数分别是员工编号、员工姓名和员工特征码。员工类将作为系统的最重要的数据模型,以对象的方式保存每一位员工的信息。
员工类的代码如下(代码位置:资源包\TM\sl\16\clock\entity\organizations.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQx8nkgH-1639056600153)(OpenCV进阶篇01.assets/image-20211128211042060.png)]

2.全局变量
organizations.py中的全局变量较多,主要用来当作系统缓存保存所有数据。这些全局代码包括:

LOCK_RECORD 实时保存员工的打卡记录。

EMPLOYEES 实时保存所有员工信息。

MAX_ID 记录当前最大ID,可在录入新员工时,为新员工分配新ID。

CODE_LEN 开发者可以通过修改CODE_LEN的值来控制员工特征码的长度,默认长度为6位。

WORK_TIME 上班时间,用来判断员工打卡情况。程序启动时由IO流模块为其赋值。

CLOSING_TIME 下班时间,功能同WORK_TIME。

USERS 系统所有管理员的账号和密码字典,用于校验用户输入的管理员账号和密码。

这些全局代码如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pNBeFauM-1639056600153)(OpenCV进阶篇01.assets/image-20211128211155281.png)]

3.增删员工
organizations.py提供了添加新员工和删除员工的方法,其他模块需要调用这些方法来进行增删操作,不应直接修改EMPLOYEES列表中的数据。
add()方法用于向组织中增加新员工,因为不需要对数据做校验,所以方法中的代码非常少。该方法代码如下:

 # 添加新员工def add(e: Employee):EMPLOYEES.append(e)

remove()方法用于删除组织中的员工,参数为员工编号。方法遍历员工列表,找到该员工之后,将该员工删除,如果该员工有过打卡记录,同时将其打卡记录删除,该方法代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TH0LIhit-1639056600153)(OpenCV进阶篇01.assets/image-20211128211222751.png)]

4.分配ID
员工编号是员工的唯一标识,有新员工加入时,应为其分配最新编号。
get_new_id()方法用于生成新员工编号,其生成规则为“当前最大的员工编号+1”,这样可以保证所有编号都不重复,该方法代码如下):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EmXA5YeM-1639056600153)(OpenCV进阶篇01.assets/image-20211128211305025.png)]

16.5 工具模块设计

本系统的工具模块包含3个文件:public_tools.py、io_tools.py和camera.py。本节将详细介绍这3个文件中的代码。

16.5.1 公共工具模块

uitl文件夹下的public_tools.py就是本程序的公共工具模块,该模块提供了以下功能。 生成随机数和随机特征码。 校验时间字符串格式。
下面详细介绍public_tools.py中的代码。

1.导入模块
公共工具涉及随机数和日期格式,所以导入random和datetime两个服务模块。生成随机特征码需要通过organizations.py获取特征码长度,所以也要导入数据实体模块,代码如下:

 import randomimport datetimefrom entity import organizations as o2.生成随机数

特征码、照片文件名和验证码都用到了随机数,公共工具模块提供了一个生成指定位数数字的randomNumber()方法,其参数就是数字的位数。例如,参数为4,生成的参数就是4位数,且不会以0开头。该方法最后返回的是字符串类。
randomNumber()方法的具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdI6LOsQ-1639056600154)(OpenCV进阶篇01.assets/image-20211128211436263.png)]

特征码实际上是长度固定的随机码,特征码的程度保存在数据实体模块的CODE_LEN变量中,可以直接调用randomNumber(CODE_LEN)创建特征码。特征码最好保持6位以上,这样才能降低特征码重复的概率。
randomCode()就是生成特征码的方法,该方法代码如下# 随机生成与特征码长度相等的数字
def randomCode():
return randomNumber(o.CODE_LEN) # 特征码的长度

3.校验时间格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMk9If3u-1639056600154)(OpenCV进阶篇01.assets/image-20211128211533748.png)]

16.5.2 IO流模块

uitl文件夹下的io_tools.py是本程序的IO流工具模块,该模块提供了以下功能。

封装所有对文件的读写操作,包括加载员工信息、加载打卡记录、加载照片文件、删除员工信息、删除打卡记录等。 文件自检功能。 创建CSV文件。
下面详细介绍io_tools.py中的代码。

1.导入模块
IO流工具将文件中的数据保存到数据实体模块中,需导入os模块和organizations.py文件。因为删除图片需要员工特征码,所以需要人事服务模块提供相关功能,代码如下:

 from service import hr_service as hrfrom entity import organizations as ofrom service import recognize_service as rsimport osimport cv2import numpy as np

2.全局变量
全局变量中保存了各个数据文件配置,包含文件路径、文件名和照片的宽和高。这里使用了os模块提供的os.getcwd()方法来获取项目根目录。全局变量的代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IKXPHmeu-1639056600154)(OpenCV进阶篇01.assets/image-20211128211636692.png)]

3.文件自检方法
为了防止用户误删数据文件而导致程序无法正常运行,公共工具模块提供了checking_data_files()文件自检方法。该方法在程序启动时执行,然后自动检查所有数据文件的状态,如果发现丢失文件(或文件夹),就会自动创建新的空数据文件(或文件夹)。该方法代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDetmbz7-1639056600155)(OpenCV进阶篇01.assets/image-20211128211707043.png)]

4.从文件中加载数据。

本系统中的所有数据都保存在文本文件中,当程序启动时,需要加载所有数据,包括员工信息、员工打卡记录和员工照片。这3类数据都有各自的加载方法。
load_employee_info()是加载员工信息的方法,该方法读取全局变量指定的员工信息文件,将文件中的内容逐行读取,然后通过英文逗号分隔,根据分隔出的数据创建员工对象,最后把员工对象保存在员工列表中。这样就完成了员工信息的加载。
在读取员工数据的同时,该方法也会记录出现过的最大员工编号,并将最大员工编号赋值给数据实体模块。
load_employee_info()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrfZXnz6-1639056600156)(OpenCV进阶篇01.assets/image-20211128211741820.png)]

load_lock_record()是加载员工打卡记录的方法。该方法读取全局变量指定的打卡记录文件,因为文件保存的是打卡记录字典的字符串内容,所以直接将文件中所有文本读出,然后转换成字典类型,最后将转换后的字典对象直接赋值数据实体模块即可。
load_lock_record()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LA6Zg5GY-1639056600156)(OpenCV进阶篇01.assets/image-20211128211758371.png)]

load_employee_pic()是加载员工照片文件的方法,该方法首先遍历全局变量指定的照片文件夹,读取每一张照片文件并封装成OpenCV中的图像对象,然后从文件名中截取特征码,将特征码作为人脸识别的标签,最后将图像、标签统一提交人脸识别器进行训练。load_employee_pic()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S1NTIKgW-1639056600156)(OpenCV进阶篇01.assets/image-20211128211819740.png)]

load_work_time_config()是上下班时间配置文件的方法。因为配置文件中保存的数据格式非常简单,所以该方法直接将文件中所有内容读取出来,按照“/”字符截取,并将截取的数据赋值数据实体的全局变量。
load_work_time_config()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3uxijjXQ-1639056600156)(OpenCV进阶篇01.assets/image-20211128212248428.png)]

load_users()是加载管理员账号密码文件的方法。因为文件保存的是管理员账号和密码字典的字符串内容,所以直接将文件中所有文本读出来,然后转换成字典类型,最后将转换之后的字典对象直接赋值数据实体模块即可
load_users()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVH58PdC-1639056600157)(OpenCV进阶篇01.assets/image-20211128212304806.png)]

5.将数据保存到文件中
既然有加载数据的方法,也就应该有保存数据的方法。当数据发生变化时,程序应立即将变化后的数据保存到本地硬盘上。公共工具模块提供了2种将数据保存到文件中的方法(保存新员工照片的方法由摄像头工具模块提供)。
save_employee_all()方法可以将员工列表中的数据保存到员工数据文件中。该方法首先打开文件的写权限,以覆盖的方式替换文件中的内容,然后遍历所有员工,将员工信息通过英文逗号和换行符拼接到一起,最后将拼接的文本写入文件中。
save_employee_all()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8HPTmPH-1639056600157)(OpenCV进阶篇01.assets/image-20211128212324236.png)]

save_lock_record()方法可以将打卡记录字典中的数据保存到打卡记录数据文件中,其逻辑与保存员工数据的方法类似,只不过不需要拆分或拼接数据,而是直接把字典对象转换成字符串,将转换得到的字符串覆盖到打卡记录数据文件中。
save_lock_record()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VZE4qyxB-1639056600157)(OpenCV进阶篇01.assets/image-20211128211903408.png)]

save_work_time_config()方法可以将数据实体中的上班时间和下班时间保存到文件中。先按照“上班时间/下班时间”格式拼接2个时间的字符串,然后将拼接好的内容写入上下班配置文件中。
save_work_time_config ()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sc3BortO-1639056600157)(OpenCV进阶篇01.assets/image-20211128212417032.png)]

6.删除照片
当一名员工被删除,该员工的照片就成了系统的垃圾文件,若不及时清除不仅会占用空间,还会加重人脸识别器的训练成本。
remove_pics()方法就是公共工具模块提供的删除指定员工照片的方法,参数为被删除的员工编号。该方法首先通过员工编号获取该员工的特征码,然后到照片文件夹中遍历所有文件,只要文件名以此员工的特征码开头,就将文件删除。删除后在控制台打印删除日志以提醒用户。
remove_pics()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\io_tools.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHBMp6L1-1639056600158)(OpenCV进阶篇01.assets/image-20211128212439429.png)]

7.生成CSV文件
考勤月报是一个内容非常多的报表,不适合在控制台中展示,但很适合生成Excel报表来展示。因为使用Python技术创建Excel文件需要下载并导入第三方模块,会加重读者的学习压力,所以这里使用更简单的CSV格式文件来展示报表。Excel可以直接打开CSV文件。
CSV文件实际上是一个文本文件,每一行文字都对应Excel中的一行内容。CSV文件将每一行文字内容用英文逗号分隔,Excel根据这些英文逗号自动将文字内容分配到每一列中。
create_CSV()方法专门用来创建CSV文件,第一个参数是CSV文件的文件名,这个名称不包含后缀;第二个参数是CSV文件写入的文本内容。方法会将CSV文件生成在/data/文件夹下,因为大部分电脑都是用Windows系统,所以按照gbk字符编码写入内容,这样可以保证Windows系统下使用Excel打开CSV文件不会发生乱码。
create_CSV()方法的具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dTetFLFc-1639056600158)(OpenCV进阶篇01.assets/image-20211128212514367.png)]

16.5.3 摄像头工具模块

uitl文件夹下的camera.py是本程序的摄像头工具模块,该模块提供了以下功能: 开启摄像头打卡。 开启摄像头为员工拍照。
下面详细介绍camera.py中的代码。

1.导入模块
摄像头模块需要调用OpenCV和人脸识别服务的方法来实现拍照和视频打卡功能。因为打卡成功后要显示员工姓名,所以还需调用人事服务模块提供的方法,代码如下(代码位置:资源包\TM\sl\16\clock\util\camera.py):

 import cv2from util import public_tools as toolfrom util import io_tools as iofrom service import recognize_service as rsfrom service import hr_service as hr

2.全局变量
录入新用户时需为新用户拍照,用户通过按键盘按键完成拍照。全局变量保存了键盘上Esc键和Enter键的ASCII码,OpenCV对比这2个变量来判断用户按了哪个按键,代码如下(代码位置:资源包\TM\sl\16\clock\util\camera.py):

 ESC_KEY = 27    # Esc键的ASCII码ENTER_KEY = 13  # Enter键的ASCII码3.为新员工拍照

执行register()方法开启本地默认摄像头,方法参数是被拍照员工的特征码,当用户按Enter键时,该方法把摄像头的当前帧画面保存成图像文件,文件名以该员工特征码开头。每名新员工需要拍3张图片,也就是需要按3次Enter键,该方法才会结束。最后员工拍摄的照片都保存在/data/face/文件夹中,如图16.8所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJwCKBTy-1639056600158)(OpenCV进阶篇01.assets/image-20211128212615823.png)]

图16.8 /data/face/文件夹中员工照片文件
register()方法的具体代码如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oViS48u-1639056600158)(OpenCV进阶篇01.assets/image-20211128212655013.png)]

4.开启摄像头打卡
执行clock_in()方法开启本地默认摄像头,程序扫描摄像头每一帧画面里是否有人脸,如果有人脸,就将这一帧画面与所有员工照片样本做比对,判断当前画面里的人脸属于哪位员工。人脸识别服务给出识别成功的特征码,通过特征码获得员工姓名,最后将识别成功的员工姓名返回。如果屏幕中没有出现人脸或者识别不成功,摄像头会一直处于开启状态。
clock_in()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\util\camera.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ja7Csmk2-1639056600159)(OpenCV进阶篇01.assets/image-20211128212713423.png)]

16.6 服务模块设计

本系统的服务模块包含2个文件:hr_service.py和recognize_service.py。前者提供所有人事管理的相关功能,例如增减员工、查询员工数据;后者提供人脸识别服务。本节将详细介绍这2个文件中的代码。

16.6.1 人事服务模块

service文件夹下的hr_service.py就是本程序的人事服务模块,该模块专门处理所有人事管理方面的业务,包含以下功能。

添加新员工。

删除某员工。

为指定员工添加打卡记录。

多种获取员工信息的方法。

生成考勤日报。

生成考勤月报(CSV文件)。

下面详细介绍hr_service.py中的代码。

1.导入模块
人事服务需要管理员工类列表、记录打卡时间,还要计算、对比负责的日期和时间数值,所以要导入数据实体模块、公共工具模块、时间模块和日历模块。代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

 from entity import organizations as ofrom util import public_tools as toolfrom util import io_tools as ioimport datetimeimport calendar

2.加载所有数据

程序启动的首要任务就是加载数据,人事服务模块将所有加载数据的方法封装成load_emp_data()方法,程序启动时运行此方法就可以一次性载入所有保存在文件中的数据。该方法依次进行文件自检,载入管理员账号密码、打卡记录、员工信息和员工照片。
load_emp_data()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eL5xG93C-1639056600159)(OpenCV进阶篇01.assets/image-20211128212820407.png)]

3.添加新员工
add_new_employee()方法用于添加新员工,参数为新员工的姓名。该方法通过公共工具模块创建随机特征码,通过数据实体模块创建新员工编号,然后结合姓名参数创建新

员工对象,在员工列表中添加新员工对象,并将最新的员工列表写入员工数据文件中,最后将该员工的特征码返回,摄像头服务根据此特征码为员工创建照片文件。
add_new_employee()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x41vlsDG-1639056600159)(OpenCV进阶篇01.assets/image-20211128212859144.png)]

4.删除员工
remove_employee()方法用来删除已有的员工资料,参数为被删除员工的编号。该方法首先删除该员工的所有照片文件,然后在员工列表中清除该员工的所有信息,包括打卡记录,最后将当前员工列表和打卡记录覆盖到数据文件中。这样数据文件里不会再有该员工的任何信息了。
remove_employee()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z8fwr15P-1639056600160)(OpenCV进阶篇01.assets/image-20211128212918378.png)]

5.添加打卡记录
add_lock_record()方法用来为指定员工添加打卡记录,参数为员工的姓名。如果某个员工打卡成功,该方法首先检查该员工是否有已经存在的打卡记录,如果没有记录就为其创建新记录,如果有记录就在原有记录上追加新时间字符串。该方法最后把当前打卡记录保存到数据文件中。
add_lock_record()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uiDDwMB4-1639056600160)(OpenCV进阶篇01.assets/image-20211128212933763.png)]

6.获取员工数据
人事服务提供了多种获取员工数据的方法,可以满足多种业务场景,下面分别介绍。
get_employee_report()方法可以返回一个包含所有员工简要信息的报表,可用于在前端展示员工列表,该方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-07NQLvF8-1639056600160)(OpenCV进阶篇01.assets/image-20211128213013377.png)]

删除员工操作需输入被删除员工的编号,程序对用户输入的值进行校验,如果用户输入的员工编号不在员工列表之中(即无效编号),就认为用户操作有误,程序中断此业务。
check_id()方法用来判断输入的编号是否有效,编号如果有效就返回True,无效就返回False,该方法的代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-saZObEJD-1639056600160)(OpenCV进阶篇01.assets/image-20211128213031342.png)]

通过员工特征码获取该员工姓名代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9R50wUjH-1639056600161)(OpenCV进阶篇01.assets/image-20211128213059725.png)]

通过员工编号获取该员工特征码的代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HddPqFEA-1639056600161)(OpenCV进阶篇01.assets/image-20211128213116746.png)]

7.验证管理员账号和密码
valid_user()方法用来验证管理员的账号和密码,第一个参数为管理员账号,第二个参数为管理员密码。该方法首先判断输入的管理员账号是否存在,如果存在则再比对输入的密码,只有管理员账号存在且密码正确的情况下,该方法才返回True,其他情况返回False。

valid_user()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtS0htF5-1639056600161)(OpenCV进阶篇01.assets/image-20211128213220573.png)]

8.保存上下班时间
save_work_time()方法用来保存用户设置的上下班时间,第一个参数为上班时间,第二个参数为下班时间,2个参数均为字符串,且必须符合“%H:%M:%S”时间格式,例如08:00:00。该方法直接修改数据实体中的全局变量,所以用户可以修改实时的上下班时间,即设置时间之后,日报和月报会立即使用新的时间分析考勤数据。
save_work_time()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

 # 保存上下班时间def save_work_time(work_time, close_time):o.WORK_TIME = work_timeo.CLOSING_TIME = close_timeio.save_work_time_config()  # 上下班时间保存到文件中

9.打印考勤日报

打印考勤日报的方法有2个:get_day_report()方法打印指定日期的日报,get_today_report()方法打印今天的日报。下面分别介绍。
get_day_report()方法打印哪一天的日报是由参数date决定的,参数d ate是一个字符串,且必须符合“%Y-%m-%d”时间格式,例如“2008-08-08”。该方法创建date指定的时间对象,分别计算这一天0点、12点和23点59分59秒的时间对象,并且会根据用户设置的上下班时间计算这一天上班时间对象和下班时间对象,这些时间对象将用来分析员工的考勤情况。员工的打卡规则如表16.2所示。

​ 表16.2 打卡规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2ujSygr-1639056600161)(OpenCV进阶篇01.assets/image-20211128213246503.png)]

方法中分别创建了迟到、早退和缺席名单3个列表,只要某员工出现不正常打卡记录,就会将该员工姓名放到对应不正常打卡状态的名单里,最后打印报表,给出各名单人数和明细。
get_day_report()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7lQGqvzk-1639056600162)(OpenCV进阶篇01.assets/image-20211128213913981.png)]

因为负责考勤的用户最常查看的就是当天的打卡情况,所以将当天打卡日报单独封装成get_today_report()方法。该方法自动生成当天的date字符串,并将其作为参数调用get_day_report()方法。
get_today_report()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oohNAeag-1639056600162)(OpenCV进阶篇01.assets/image-20211128213952660.png)]

10.生成考勤月报
与考勤日报不同,考勤月报是一种汇总形式的报表,可以展示员工整个月的考勤状况。因为月报表内容较多,所以不会在控制台中展示,而是生成独立的报表文件。
生成考勤月报的方法有2个:get_month_report ()方法生成指定月份的月报;get_pre_month_report ()方法打印上个月的月报。下面分别介绍。
考勤月报的校验逻辑与考勤日报基本相同,相当于一次性统计了一个月的日报数据。唯一不同的是统计月报的时候不是创建异常打卡名单,而是统计每一位员工每一天的打卡情况。每个员工的打卡情况用一个字符串表示,如有正常打卡,就追加正常打卡的标记,如果迟到就追加迟到标记,以此类推。统计完所有员工一个月打卡情况之后再对每个字符串进行分析。

如果员工在×日有正常上下班打卡标记,则月报×日下不显示任何内容。迟到或早退标记都被忽略,因为可能是员工误打卡。

如果员工在×日没有上班打卡标记,且有迟到标记,则在月报×日下显示【迟到】。

如果员工在×日没有下班打卡标记,且有早退标记,则月报×日下显示【早退】。

如果员工在×日没有上班打卡标记,也没有迟到标记,则在月报×日下显示【上班未打卡】。

如果员工在×日没有下班打卡标记,也没有早退标记,则在月报×日下显示【下班未打卡】。

如果员工在×日没有任何打卡标记,则在月报×日下显示【缺席】。
月报采用CSV格式文件展示,CSV文件自动生成在项目的/data/文件夹下。CSV是文本文件,用换行符区分表格的行,用英文逗号区分表格的列。get_month_report()方法最后将生成的CSV格式月报用记事本打开,其效果如图16.9所示,如果用Office Excel打开则可以看到正常的表格内容,效果如图16.10所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yL9nCVMB-1639056600162)(OpenCV进阶篇01.assets/image-20211128225917095.png)]

​ 图16.9 用记事本打开CSV格式的月报

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xyIK1dAd-1639056600162)(OpenCV进阶篇01.assets/image-20211128225941244.png)]

​ 图16.10 用Office Excel打开CSV格式的月报
get_month_report()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXPYGowt-1639056600163)(OpenCV进阶篇01.assets/image-20211128230016478.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwPZhZhD-1639056600163)(OpenCV进阶篇01.assets/image-20211128230040789.png)]

因为负责考勤的用户最常查看上个月的月报,所以将生成上个月月报单独封装成了get_pre_month_report()方法。该方法自动生成上个月的pre_month字符串,并将其作为参数调用get_month_report()方法。
get_pre_month_report()具体代码如下(代码位置:资源包\TM\sl\16\clock\service\hr_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVte0XN3-1639056600163)(OpenCV进阶篇01.assets/image-20211128230117420.png)]

16.6.2 人脸识别服务模块

service文件夹下的recognize_service.py就是本程序的人脸识别服务模块,该模块提供人脸识别算法,其包含以下功能。 检测图像中是否有正面人脸。 判断图像中的人脸属于哪个人。
下面详细介绍recognize_service.py中的代码。

1.导入包
人脸识别服务需要导入OpenCV相关模块和os模块,代码如下(代码位置:资源包\TM\sl\16\clock\service\recognize_service.py):

import cv2
import numpy as np
import os

2.全局变量
全局变量中创建了人脸识别器引擎和人脸识别级联分类器对象,PASS_CONF为人脸识别的信用评分,只有低于这个值的人脸识别评分才认为相似度高。全局变量的代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVRKnYsa-1639056600163)(OpenCV进阶篇01.assets/image-20211128230309914.png)]

3.训练识别器
train()方法专门用来训练人脸识别器,该方法仅封装了识别器对象的训练方法,方法参数为样本图像列表和标签列表,其代码如下(代码位置:资源包\TM\sl\16\clock\service\recognize_service.py):

 # 训练识别器def train(photos, lables):RECOGNIZER.train(photos, np.array(lables))  # 识别器开始训练

4.发现人脸

found_face()方法用来判断图像中是否有正面人脸,参数为灰度图像。通过正面人脸级联分类器对象检测图像中出现的人脸数量,最后返回人脸数量大于0的判断结果,有人脸就返回True,没有就返回False。
found_face()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\recognize_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yjnlSO3L-1639056600164)(OpenCV进阶篇01.assets/image-20211128230328108.png)]

5.识别人脸
recognise_face()方法用来识别图像中的人脸属于哪位员工,方法参数为被识别的图像。该方法必须在识别器接受完训练之后被调用。识别器给出分析得出的评分,如果评分大于可信范围,则认为图像中不存在任何已有员工,返回-1,否则返回已有员工的特征码。
recognise_face()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\service\recognize_service.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcxmjFO5-1639056600164)(OpenCV进阶篇01.assets/image-20211128230421418.png)]

16.7 程序入口设计

main.py是整个程序的入口文件,负责在控制台中打印菜单界面,用户通过指令可以使用系统中的全部功能,包括打卡、员工管理等,所以会有大量指令判断逻辑。
main.py需要导入摄像头工具模块、公共工具模块和人事服务模块。代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

 from util import camerafrom util import public_tools as toolfrom service import hr_service as hr

下面详细介绍main.py中的代码

16.7.1 用户权限管理

系统中除了打卡和退出2项功能可以随意使用,其他菜单都需要管理员权限才能使用。用户选中查看记录、员工管理和考勤报表菜单,系统会验证用户身份,如果不是管理员身份就会弹出管理员登录提示,用户输入正确的账号和密码才可以继续使用这些功能。
main.py文件中定义了一个全局变量ADMIN_LOGIN,该变量表示管理员的登录状态,默认为False,即管理员未登录。其代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

 ADMIN_LOGIN = False  # 管理员登录状态

login()为管理员登录方法,该方法弹出输入管理员账号和密码的提示,如果用户输入账号为字符串“0”,则认为用户取消了登录操作。如果用户输入了正确的账号和密码,就将全局变量ADMIN_LOGIN的值改为True,即管理员已处于登录状态,这样系统就会开放所有已设权限的功能,用户可以随意使用。
login()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EaeWbdAR-1639056600164)(OpenCV进阶篇01.assets/image-20211128230514889.png)]

16.7.2 主菜单设计

start()方法是程序的启动方法,在初始化方法执行完毕后执行。该方法在控制台中打印程序的主功能菜单,效果如图16.11所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xe0gh9hT-1639056600164)(OpenCV进阶篇01.assets/image-20211128230537548.png)]

​ 图16.11 主菜单
此时用户需先输入菜单对应的数字,再按Enter键进入具体功能菜单中。如果用户输入的数字不在功能菜单中,则提示指令有误,请用户重新输入。
如果当前用户没有管理员权限,在选中查看记录、员工管理和考勤报表菜单时会要求用户先登录管理员的账号,效果如图16.12所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMLp6D0P-1639056600165)(OpenCV进阶篇01.assets/image-20211128230618875.png)]

​ 图16.12 用户需登录管理员账号才能使用员工管理功能
start()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5D9SSIyP-1639056600165)(OpenCV进阶篇01.assets/image-20211128230644783.png)]

16.7.3 人脸打卡功能

face_clock()是人脸打卡功能的执行方法,该方法调用摄像头工具模块提供的打卡方法,此时只要用户面向摄像头,摄像头即可自动扫描人脸并识别特征,效果如图16.13所示。如果镜头中的人脸符合某个员工的特征,则会返回该员工姓名,然后调用人事服务模块为此员工添加打卡记录,最后提示该员工打卡成功,过程如图16.14所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDLJYRj4-1639056600165)(OpenCV进阶篇01.assets/image-20211128230722320.png)]

​ 图16.13 打卡者需正向面对镜头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7yLXJM4-1639056600165)(OpenCV进阶篇01.assets/image-20211128230745651.png)]

​ 图16.14 员工王五打卡成功
face_clock()方法的具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9n2YuCv-1639056600166)(OpenCV进阶篇01.assets/image-20211128230807096.png)]

16.7.4 为新员工登记人脸照片样本

employee_management()方法是员工管理功能的执行方法,该方法在控制台打印员工管理功能菜单,如图16.15所示。输入菜单对应的数字,再按Enter键进入具体功能菜单中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84RvmE3O-1639056600166)(OpenCV进阶篇01.assets/image-20211128230830763.png)]

​ 图16.15 员工管理功能菜单
录入新员工的过程如图16.16所示。如果用户在员工管理功能菜单中输入数字1并按Enter键,则开始执行新员工录入操作。首先输入新员工名称,输入完毕后程序打开默认摄像头,此时让新员工面对摄像头,程序将摄像头拍摄的照片展示在如图16.17所示的register窗口中。在register窗口上按3次Enter键,自动保存3张摄像头拍摄的照片文件,最后提示录入成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GwZLOuVG-1639056600166)(OpenCV进阶篇01.assets/image-20211128230908335.png)]

​ 图16.16 录入新员工的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxbE4bHg-1639056600166)(OpenCV进阶篇01.assets/image-20211128230931356.png)]

​ 图16.17 register窗口展示的员工照片

16.7.5 删除员工全部数据

如果用户在员工管理功能菜单中输入数字2并按Enter键,则开始执行删除员工操作。首先程序会将所有员工的名单打印到控制台中,用户输入要删除的员工编号并按Enter键,程序给出一个验证码让用户输入,如果用户输入的验证码正确,该员工的员工信息、打卡记录和照片文件都会被删除,如果用户输入的验证码错误,则会取消删除员工操作,员工数据不会丢失。删除员工操作的过程如图16.18所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wTUX1EAG-1639056600167)(OpenCV进阶篇01.assets/image-20211128230958321.png)]

​ 图16.18 删除员工操作的过程
employee_management()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7iZ2pZFq-1639056600167)(OpenCV进阶篇01.assets/image-20211128231025347.png)]

16.7.6 查询员工打卡记录

check_record()方法是查询记录功能的执行方法,该方法在控制台打印查询记录功能菜单,效果如图16.19所示。此时用户需先输入菜单对应的数字,再按Enter键进入具体功能菜单。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfLsa9wM-1639056600167)(OpenCV进阶篇01.assets/image-20211128231100025.png)]

​ 图16.19 查看记录功能菜单
如果用户在查询记录功能菜单中输入数字1并按Enter键,程序将所有员工列表打印到控制台中,效果如图16.20所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YTqzNJCK-1639056600168)(OpenCV进阶篇01.assets/image-20211128231120571.png)]

​ 图16.20 查看员工列表

如果用户在查询记录功能菜单中输入数字2并按Enter键,程序将所有员工的打卡记录打印到控制台中,效果如图16.21所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lkLIEDX-1639056600169)(OpenCV进阶篇01.assets/image-20211128231213893.png)]

​ 图16.21 查看打卡记录
check_record()方法的具体的代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z0aJnt9U-1639056600169)(OpenCV进阶篇01.assets/image-20211128231247326.png)]

16.7.7 生成考勤报表

check_report()方法是考勤报表功能的执行方法,该方法在控制台打印考勤报表功能菜单,效果如图16.22所示。此时用户需先输入菜单对应的数字,再按Enter键进入具体功能菜单。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eaGRRnCR-1639056600169)(OpenCV进阶篇01.assets/image-20211128231405373.png)]

​ 图16.22 考勤报表功能菜单
如果用户在考勤报表功能菜单中输入数字1并按Enter键,则会提示用户输入日期。用户按照指定格式输入日期后即可看到该日期的考勤日报。如果用户输入数字0,可以打印当天的考勤日报。例如,打印2021年3月2日考勤日报的效果如图16.23所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QiA4MO20-1639056600169)(OpenCV进阶篇01.assets/image-20211128231458581.png)]

​ 图16.23 打印2021年3月2日的考勤日报
如果用户在考勤报表功能菜单中输入数字2并按Enter键,则提示用户输入月份。用户按照指定格式输入月份后,即可生成该月考勤月报,并显示生成的月报文件地址。如果用户输入数字0,可以生成上个月的考勤月报。例如,生成2021年3月考勤月报的效果如图16.24所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zAJIYh1q-1639056600170)(OpenCV进阶篇01.assets/image-20211128231521785.png)]

​ 图16.24 生成2021年3月考勤月报
图16.24中提示“2021年3月考勤月报.csv”文件保存在D:\clock\data\文件夹中,打开这个文件夹即可以看到月报文件,如图16.25所示,用Office Excel打开月报即可以看到如图16.26所示的表格内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5CR8QAB-1639056600170)(OpenCV进阶篇01.assets/image-20211128231550636.png)]

​ 图16.25 CSV文件的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6n3UUPC-1639056600170)(OpenCV进阶篇01.assets/image-20211128231612666.png)]

​ 图16.26 使用Office Excel打开月报的效果
check_report()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4KO3CWB-1639056600170)(OpenCV进阶篇01.assets/image-20211128231638940.png)]

16.7.8 自定义上下班时间

report_config()方法是报表设置功能的执行方法,如果用户在考勤报表功能菜单中输入数字3并按Enter键,则进入报表设置功能菜单,效果如图16.27所示,在这个菜单中可以设置用于分析考勤记录的上下班时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Poc9iD9t-1639056600171)(OpenCV进阶篇01.assets/image-20211128231711120.png)]

​ 图16.27 报表设置功能菜单
如果用户在报表设置功能菜单中输入数字1并按Enter键,则分别提示用户输入上班时间和下班时间,效果如图16.28所示。如果用户输入的时间格式错误,程序要求用户重新输入。当用户设置完后,上下班时间立即生效,此时再打印考勤报表就会按照最新的上下班时间进行分析。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtTtxdhx-1639056600171)(OpenCV进阶篇01.assets/image-20211128231731240.png)]

​ 图16.28 用户设置上班时间和下班时间

report_config()方法的具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8zWP94T-1639056600171)(OpenCV进阶篇01.assets/image-20211128231810922.png)]

16.7.9 启动程序

main.py定义完所有全局变量和方法之后,代码的最下方就是整个系统的启动脚本:首先执行系统初始化操作,然后启动系统。具体代码如下(代码位置:资源包\TM\sl\16\clock\main.py):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z0ZJS5hi-1639056600171)(OpenCV进阶篇01.assets/image-20211128231824542.png)]

16.8 小结

本章详细介绍了一个完整小型项目的开发流程。这个项目主要包括5大功能:打卡、退出、查看记录、员工管理和考勤报表。其中,查看记录、员工管理和考勤报表3个功能需要管理员权限才能使用。这个项目采用命令提示符窗口实现与计算机之间的交互。虽然命令提示符窗口有些简陋,但不影响这个项目的实用价值。如果读者想制作一个绚丽的窗口界面运行这个项目,可以在掌握这个项目的功能结构、业务流程和实现原理后,尝试用Python PyQt5的相关知识予以实现。

OpenCV进阶篇视频相关推荐

  1. opencv进阶篇---银行卡数字识别

    执行结果: 主要思想:对模板图像以及待检测图像进行外轮廓检测,并得到各自外接矩形,将模板图像的外接矩形做resize()操作,使其外接矩形的大小与待检测图像外接矩形的大小相一致,然后与待检测图形做模板 ...

  2. 36篇博文带你学完opencv :python+opencv进阶版学习笔记目录

    基础版学习笔记传送门 36篇博文带你学完opencv :python3+opencv学习笔记汇总目录(基础版) 进阶版笔记 项目 opencv进阶学习笔记1: 调用摄像头用法大全(打开摄像头,打开摄像 ...

  3. 【树莓派4B深度学习 垃圾分类】Chap.3 树莓派安装opencv并测试视频接口实时视频流的垃圾分类【深度学习 招式篇】

    [树莓派4B深度学习 垃圾分类]Chap.3 树莓派安装opencv并测试视频接口实时视频流的垃圾分类[深度学习 招式篇] 后记 1.激活虚拟环境+进入代码+更改测试集路径(这里是安装成功后测试的代码 ...

  4. UG/NX10二次开发学习视频目录整理(NXOPEN进阶篇)

    为了方便搜索需要的视频资料,整理了唐康林老师发布在B站的视频目录,支持全局目录搜索,点击直达视频. NX10二次开发(NXOPEN进阶篇) P1第01章-01-打算学习本门课程先看看这讲 P2第01章 ...

  5. 视频教程-C语言程序设计--进阶篇教学视频-C/C++

    C语言程序设计--进阶篇教学视频 烟台大学计算机学院教师,二十年余教师生涯,看出了在错综复杂的教育环境中,坚持教育教学的价值与前景.和学生并肩,与不良学风作斗争,为IT菜鸟建跑道,让大一的孩子会编程, ...

  6. Go语言-进阶篇-欧阳桫-专题视频课程

    Go语言-进阶篇-343人已学习 课程介绍         区块链第一语言,Web新贵: 兼具Python的简洁与C++的强大: 用超多好玩的小例子,带你打开通向世界2.0的大门: 风格依旧水煮,依旧 ...

  7. Python趣味百题-进阶篇-刘硕-专题视频课程

    Python趣味百题-进阶篇-11679人已学习 课程介绍         精选的30个实例都从趣味编程的角度出发,并兼顾实用性.实例涵盖了Python程序设计的基础知识和常用算法,很多实例来自编程大 ...

  8. Jenkins进阶篇-臧雪园-专题视频课程

    Jenkins进阶篇-179人已学习 课程介绍         课程内容首先解了持续集成概念:软件的开发生命周期.模型的发展历程.及持续集成由哪些组件进行集成.和公司实行持续集成为公司带来哪些方面受益 ...

  9. 计算机编程书籍-笨办法学Python 3:基础篇+进阶篇

    编辑推荐: 适读人群 :本书适合所有已经开始使用Python的技术人员,包括初级开发人员和已经升级到Python 3.6版本以上的经验丰富的Python程序员. "笨办法学"系列, ...

最新文章

  1. matlab立方体投影,那些投影到三维的高维立方体,后来都怎么样了?(浅度好文)...
  2. 荐读:五月最值得阅读的15篇人工智能文章
  3. 【数理知识】《积分变换与场论》王振老师-第5章-场论
  4. ffmpeg的map参数
  5. 作者:黄伟(1964-),男,博士,西安交通大学管理学院教授、博士生导师、院长...
  6. Windows10 环境下Jupyter Notebook的安装与使用
  7. 【多题合集】AC自动机练习,被HDU支配的恐惧
  8. IOS开发笔记 - 先有鸡,还是先有蛋?相互引用的奇遇!
  9. js中的splice方法使用,删除数组中的最大最小值
  10. 深度学习论文: Slicing Aided Hyper Inference and Fine-tuning for Small Object Detection及其PyTorch实现
  11. 51单片机蜂鸣器播放音乐C语言程序实例,51单片机 使用蜂鸣器播放简单音乐
  12. Android Studio3.0对于百度地图SDK的开发(基于方向传感器实现手机朝向显示)
  13. linux 密码修改下次,Linux 强制使用者下次登入修改密码
  14. uni-app 省市区选择器
  15. 如何把win桌面的压缩包复制到虚拟机共享文件夹中
  16. 长平之战后的秦赵又一次决战——邯郸保卫战
  17. rsync命令以及xsync封装
  18. linux nginx连接memcache和ngx_http_consistent_hash负载均衡算法
  19. Android incorrect AVA format
  20. 前端html+CSS基础

热门文章

  1. mysql 优惠卷表设计_这些年MySQL表设计踩过的坑!
  2. 律师如何加强自身的计算机文化教育网,提高_计算机文化基础_教学效果的几点心得.pdf...
  3. 报告:2019-2025年,全球数据中心年复合增长率将超过7%
  4. 数据中心制冷系统41问答题
  5. 中国证券期货业南方信息技术中心二期约1.5万个机柜建设项目EPC总包定了!
  6. 对一次短路故障的分析与总结
  7. 人才短缺是数据中心运营商面临的新问题
  8. js 获取屏幕高宽_JS获取屏幕的宽高。
  9. python连接es数据库_Python Elasticsearch API操作ES集群
  10. openresty完全开发指南_送给你,PBA商业分析指南(全书下载)