第 27 章

认识物件侦测原理与资源档案

Cascade Classifier · Haar-like Features · OpenCV Haarcascades XML · 人脸、眼睛、身体、猫脸与车牌侦测
人脸辨识是计算机技术的一种,这个技术可以测出人脸在影像中的位置,同时也可以找出多个人脸,在检测过程中基本上会忽略背景或其他物体。OpenCV 有提供一系列训练测试过的资源档,这些资源档可以让我们用很简单的指令完成人脸、眼睛、身形、上半身、下半身、猫脸、车牌等检测。
第二十七章 认识物件侦测原理与资源档案封面
本章封面与小节目录。
27-1

物件侦测原理

在正式进入 AI 视觉的热门主题人脸辨识前,首先要判断目前的影像是否存在人脸,当影像存在人脸后,才可以更进一步分析此人脸是谁。

27-1-1 阶层分类器原理

Cascade Classifier 可以翻译为阶层式分类器,或是称级联分类器。这个分类器的基本原理是使用排除法,从简单开始逐步排除不符合的检测样本,经过多次检测后,最后所得到的符合我们所选的样本,又称此为正样本。

例如:假设要检测样本是否是猫,首先可以检测样本是否有 4 条腿,如果样本没有 4 条腿,则排除此样本,又称此为负样本。下一步可以检测是否有尾巴,如果没有尾巴的物件又可以排除。

阶层分类器排除法流程图
阶层式分类器依序检查条件,未通过即归为负样本,最后留下正样本。

27-1-2 Haar 特征缘由

Haar-like features 中文翻译是哈尔特征,这是用于物体辨识的数位影像特征,这个名称是来自匈牙利科学家 Alfred Haar。基础原理是使用遮罩(mask)在影像内滑动,同时计算特征值。

OpenCV 所支援的层次式分类器所采用的演算法是 2001 年 Paul Viola 和 Michael Jones 的论文 Rapid Object Detection using a Boosted Cascade of Simple Features。这是一种机器学习的演算法,阶层函数是从大量的正样本影像和负样本影像中训练出来,然后用来检测其他影像的物件。

27-1-3 哈尔特征原理

现在假设使用人脸识别为例,最初需要大量的正样本影像(人脸影像)和负样本影像(没有人脸的影像)训练分类器,计算特征值的方法采用下列 Haar 特征说明。

Haar 边界、线条与四矩形特征
Edge Features、Line Features、Four-rectangle Features 的示意。

每个特征点计算方式是黑色(书中使用蓝色绘制)部分像素总和减去白色部分的像素总和。假设一张影像使用 24 x 24 的感兴趣区块也会产生超过 160000 个特征值。为了解决这个庞大的计算,Paul Viola 和 Michael Jones 导入积分影像的观念,不论影像多大,会将特征值的计算减到只有涉及 4 个像素点的操作。

OpenCV 官方网站 Haar 特征套用在人脸的示意图
图片来源:OpenCV 官方网站。书中用此图说明眼睛区域通常比鼻子和脸颊区域更暗。

在 Paul Viola 和 Michael Jones 的论文中,即使是 200 个特征也可以提供约 95% 的准确率,最终他们设定了 6000 个特征,所以所需计算的特征一下子从 160000 减少到 6000 个特征。作者的检测器有 6000 多个特征,分成 38 个阶段,前五个阶段有 1、10、25、25 和 50 个特征。

27-2

找寻 OpenCV 的资源档案来源

OpenCV 安装成功后,可以在所安装资料夹的内看到这些资源档,以笔者的 Python 3.85 为例,可以在下列资料夹看到资源档案。

~\Python38-32\Lib\site-packages\cv2\data
Python cv2 data 资料夹内的 haarcascade XML 档案
本机 OpenCV 安装目录中的 cv2\data 资源档案。

不同版本的 Python,可能路径有差异。若找寻不到上述资料夹,也可以到 OpenCV 的 GitHub 资源托管平台下载。

https://github.com/opencv/opencv/tree/master/data/haarcascades
GitHub opencv data haarcascades 目录
OpenCV GitHub 上的 data/haarcascades 目录。
27-3

认识资源档案

每个 XML 档案就是一种已经使用哈尔(Haar featured)特征训练好的分类器,又可以称阶层式分类器档案,每个分类器可以使用在不同物件的侦测。

分类器档案名称侦测内容
haarcascade_eye.xml眼睛
haarcascade_eye_tree_eyeglasses.xml戴眼镜的眼睛
haarcascade_frontalcatface.xml正面的猫脸
haarcascade_frontalcatface_extended.xml扩充版正面的猫脸
haarcascade_frontalface_alt.xml侦测正面人脸
haarcascade_frontalface_alt_tree.xml侦测正面人脸
haarcascade_frontalface_alt2.xml侦测正面人脸
haarcascade_frontalface_default.xml侦测正面的人脸
haarcascade_fullbody.xml侦测身形
haarcascade_lefteye_2splits.xml侦测左眼
haarcascade_lowerbody.xml侦测下半身
haarcascade_profileface.xml侦测侧面的人脸
haarcascade_righteye_2splits.xml侦测右眼
haarcascade_russian_plate_number.xml侦测车牌
haarcascade_smile.xml侦测笑脸,注:测试效果不佳。
haarcascade_upperbody.xml侦测上半身

为了方便使用资源档,笔者已经将资源档案改存至 C:\opencv\data 资料夹,所以本章所有实例皆需参考该资料夹。

27-4

人脸的侦测

27-4-1 脸形阶层式分类器资源档

在阶层式分类器资源档中与正面脸形分类有关的有下列 4 个档案。

27-4-2 基础脸形侦测程式

这一节的目的是可以让程式使用 OpenCV 将影像档案的人脸标记出来,首先可以使用 CascadeClassifier() 类别下载侦测脸形的分类器资源档。

pictPath = r'C:\opencv\data\haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(pictPath)

接著需要使用辨识物件启动 detectMultiScale() 方法,语法如下:

faces = face_cascade.detectMultiScale(img, scaleFactor, minNeighbors, minSize, maxSize)
参数意义
img要辨识的影像档案。
scaleFactor若没有指定一般是 1.1,主要是指在特征比对中,图像比例的缩小倍数;必须大于 1.0。
minNeighbors每个区块的特征皆会比对,设定多少个特征数达到才算匹配成功,预设值是 3。
minSize可选参数,最小辨识区块,小于此将被忽略。
maxSize可选参数,最大的辨识区块,大于此将被忽略。

最常见的是设定前 3 个参数,例如下列表示影像物件是 imgscaleFactor 是 1.1,minNeighbors 是 3。

faces = face_cascade.detectMultiScale(img, 1.1, 3)

上述执行成功后的回传值是 faces 串列,串列的元素是元组(tuple),每个元组内有 4 组数字分别代表脸部左上角的 x 轴座标、y 轴座标、脸部的宽 w 和脸部的高 h。我们可以用 len(faces) 获得找到几张脸。

程式实例 ch27_1.py:标示影像中的人脸与找到的人脸数量
# ch27_1.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("jk.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 标注右下角底色是黄色 cv2.rectangle(img, (img.shape[1]-140, img.shape[0]-20), (img.shape[1],img.shape[0]), (0,255,255), -1) # 标注找到多少的人脸 cv2.putText(img, "Finding " + str(len(faces)) + " face", (img.shape[1]-135, img.shape[0]-5), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255,0,0), 1) # 将人脸框起来,由于有可能找到好几个脸所以用回圈绘出来 for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) # 蓝色框住人脸 cv2.imshow("Face", img) # 显示影像 cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_1.py 侦测单张人脸
使用 jk.jpg 侦测出 1 张人脸。
程式实例 ch27_2.py:使用 g5.jpg 辨识多张人脸的影像
# ch27_2.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("g5.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) cv2.rectangle(img, (img.shape[1]-140, img.shape[0]-20), (img.shape[1],img.shape[0]), (0,255,255), -1) cv2.putText(img, "Finding " + str(len(faces)) + " face", (img.shape[1]-135, img.shape[0]-5), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255,0,0), 1) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_2.py 侦测多人脸
多人脸影像执行结果,右下角标示 Finding 5 face

当然使用上述偶尔也会出现辨识不是太完美的情况,可以参考下一节实例。

27-4-3 史上最牛的物理科学家合照

在脸形检测过程,如果有多人合照时,难免也会有一些不可预期的结果产生。下列可能是当今科学界最火热的一张照片,1927 年世界著名科学家在比利时布鲁塞尔参加索维尔(Solvay)会议的合照,图片下方最中间的是爱因斯坦,下图共有 29 位科学家,其中 17 位是诺贝尔奖得主。

1927 年索维尔会议合照
图片来源维基百科:Solvay conference 1927。
程式实例 ch27_3.py:使用 solvay1927.jpg 影像侦测人脸
# ch27_3.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(pictPath) img = cv2.imread("solvay1927.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_3.py 索维尔会议合照人脸侦测
虽然 29 位科学家侦测到了 28 位,但也虚增了 4 个非人脸框。
程式实例 ch27_3_1.py:将 minNeighbors 改为 5
# ch27_3_1.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(pictPath) img = cv2.imread("solvay1927.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 5, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_3_1.py 调整 minNeighbors 后的索维尔侦测结果
情况有改善,但是有 3 个人没有抓取。

scaleFactor 是控制变数,如果更改为比较大可以减少检测的图像,不过这也会造成部分资料没有检测出来。

程式实例 ch27_4.py:改用 haarcascade_frontalface_alt.xml
# ch27_4.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_alt.xml' face_cascade = cv2.CascadeClassifier(pictPath) img = cv2.imread("solvay1927.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_4.py 使用 frontalface_alt 的索维尔侦测结果
使用 haarcascade_frontalface_alt.xml 后,人脸侦测改善许多。
程式实例 ch27_4_1.py:设定 maxSize=(50,50)
pictPath = r'C:\opencv\data\haarcascade_frontalface_alt.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("solvay1927.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20), maxSize=(50,50))
程式实例 ch27_5.py:使用 haarcascade_frontalface_alt2.xml
pictPath = r'C:\opencv\data\haarcascade_frontalface_alt2.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("solvay1927.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20), maxSize=(50,50))
程式实例 ch27_6.py:使用 haarcascade_frontalface_alt_tree.xml
# ch27_6.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalface_alt_tree.xml' face_cascade = cv2.CascadeClassifier(pictPath) img = cv2.imread("solvay1927.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_6.py 使用 frontalface_alt_tree 的索维尔侦测结果
haarcascade_frontalface_alt_tree.xml 分类器则是有比较多人脸没有侦测到。
27-5

侦测侧面的人脸

27-5-1 基础观念

当一个人脸是侧面向著镜头时,使用 27-3 节正面的人脸分类器,许多时候是无法测出此人脸的。同样是 1927 年索维尔会议的合照,另有一张后排右边算起第 4 位,因为拍照时往右看,使用前一小节的方法无法侦测到。

程式实例 ch27_6_1.py:使用 s_1927.jpg 档案重新设计 ch27_4.py
pictPath = r'C:\opencv\data\haarcascade_frontalface_alt.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("s_1927.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.02, minNeighbors = 3, minSize=(20,20))
执行结果
ch27_6_1.py 正面分类器侦测侧脸
笔者有适度编修左下方科学家的衣领,因为衣领会造成额外圈选。

27-5-2 侧面脸形侦测

在阶层式分类器资源档中与侧面脸形分类有关的档案如下:

haarcascade_profileface.xml
程式实例 ch27_6_2.py:使用 haarcascade_profileface.xml 侦测侧面人脸
# ch27_6_2.py import cv2 pictPath = r'C:\opencv\data\haarcascade_profileface.xml' face_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("s_1927.jpg") # 读取影像 faces = face_cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors = 4, minSize=(20,20))
执行结果
ch27_6_2.py 侧脸分类器侦测结果
使用侧面人脸分类器的侦测结果。
27-6

路人侦测

虽然特征档案使用的英文是 fullbody,可以翻译为身体,这个分类器笔者感觉更类似追踪路人,因为近距离的影像无法侦测,远距离的影像比较可以侦测。

27-6-1 路人侦测

路人侦测的分类器档案是 haarcascade_fullbody.xml

程式实例 ch27_7.py:路人侦测的应用
# ch27_7.py import cv2 pictPath = r'C:\opencv\data\haarcascade_fullbody.xml' body_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("people1.jpg") # 读取影像 bodies = body_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 标注身体 for (x,y,w,h) in bodies: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) # 蓝色框住身体 cv2.imshow("Body", img) # 显示影像 cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_7.py 与 ch27_8.py 路人侦测结果
左图是 people1.jpg,右图是多人群聚路人的侦测结果。
程式实例 ch27_8.py:侦测多人群聚的路人
# ch27_8.py import cv2 pictPath = r'C:\opencv\data\haarcascade_fullbody.xml' body_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("people2.jpg") # 读取影像 bodies = body_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in bodies: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Body", img) cv2.waitKey(0) cv2.destroyAllWindows()

经过测试,笔者感觉人潮不拥挤的路人侦测有比较好的效果,另外,如果是近距离的人像侦测效果也比较不好。

27-6-2 下半身的侦测

下半身侦测所使用的分类器档案是 haarcascade_lowerbody.xml

程式实例 ch27_9.py:使用 haarcascade_lowerbody.xml
# ch27_9.py import cv2 pictPath = r'C:\opencv\data\haarcascade_lowerbody.xml' body_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件

27-6-3 上半身的侦测

上半身侦测所使用的分类器档案是 haarcascade_upperbody.xml

程式实例 ch27_10.py:使用 haarcascade_upperbody.xml
pictPath = r'C:\opencv\data\haarcascade_upperbody.xml' body_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 bodies = body_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 9, minSize=(20,20))
执行结果
下半身与上半身侦测结果
下半身与上半身侦测结果;上半身实例将 minNeighbors 设为 9。
27-7

眼睛的侦测

27-7-1 眼睛分类器资源档

在分类器资源档中与眼睛分类有关的有下列 3 个档案。

27-7-2 侦测双眼实例

程式实例 ch27_11.py:使用 haarcascade_eye.xml 执行眼睛侦测
# ch27_11.py import cv2 pictPath1 = r'C:\opencv\data\haarcascade_frontalface_default.xml' pictPath2 = r'C:\opencv\data\haarcascade_eye.xml' face_cascade = cv2.CascadeClassifier(pictPath1) # 建立人脸物件 img = cv2.imread("jk.jpg") # 读取影像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 侦测人脸 faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 侦测双眼 eyes_cascade = cv2.CascadeClassifier(pictPath2) # 建立双眼物件 eyes = eyes_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 将人脸框起来,由于有可能找到好几个脸所以用回圈绘出来 for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) # 蓝色框住人脸 # 将双眼框起来,由于有可能找到好几个眼睛所以用回圈绘出来 for (x,y,w,h) in eyes: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),2) # 绿色框住眼睛 cv2.imshow("Face", img) # 显示影像 cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_11.py 眼睛侦测程式与结果
人脸以蓝色框住,眼睛以绿色框住。

上述设定 minNeighbors = 3 时,左边检测口部也被当作眼睛处理,如果改为 minNeighbors = 7 则可以得到右边的结果。另外也可以设定 maxSize 参数,让大于特定的区块抛弃。

程式实例 ch27_12.py:设定 minNeighbors = 7
# ch27_12.py import cv2 pictPath1 = r'C:\opencv\data\haarcascade_frontalface_default.xml' pictPath2 = r'C:\opencv\data\haarcascade_eye.xml' face_cascade = cv2.CascadeClassifier(pictPath1) eyes_cascade = cv2.CascadeClassifier(pictPath2) img = cv2.imread("jk.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) eyes = eyes_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 7, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) for (x,y,w,h) in eyes: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_12.py 调整 minNeighbors 后眼睛侦测结果
调整 minNeighbors 后可减少错误眼睛框。

27-7-3 侦测左眼与右眼的实例

程式实例 ch27_13.py:使用 haarcascade_lefteye_2splits.xml
# ch27_13.py import cv2 pictPath1 = r'C:\opencv\data\haarcascade_frontalface_default.xml' pictPath2 = r'C:\opencv\data\haarcascade_lefteye_2splits.xml' face_cascade = cv2.CascadeClassifier(pictPath1) # 建立人脸物件 img = cv2.imread("jk.jpg") # 读取影像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) eyes_cascade = cv2.CascadeClassifier(pictPath2) # 建立左眼物件 eyes = eyes_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 7, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) # 蓝色框住人脸 for (x,y,w,h) in eyes: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),2) # 绿色框住眼睛 cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
程式实例 ch27_14.py:使用 haarcascade_righteye_2splits.xml
# ch27_14.py import cv2 pictPath1 = r'C:\opencv\data\haarcascade_frontalface_default.xml' pictPath2 = r'C:\opencv\data\haarcascade_righteye_2splits.xml' face_cascade = cv2.CascadeClassifier(pictPath1) img = cv2.imread("jk.jpg") faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) eyes_cascade = cv2.CascadeClassifier(pictPath2) eyes = eyes_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 7, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) for (x,y,w,h) in eyes: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_13.py 与 ch27_14.py 左眼右眼侦测结果
左图与右图分别示范左眼、右眼分类器的侦测结果。
27-8

侦测猫脸

在分类器资源档中与正面猫脸分类有关的档案如下:

haarcascade_frontalcatface.xml

笔者在测试过程也发现猫脸必须正面面向镜头,比较容易侦测到。

程式实例 ch27_15.py:侦测猫脸的应用
# ch27_15.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalcatface.xml' cat_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("cat1.jpg") # 读取影像 faces = cat_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 将猫脸框起来,由于有可能找到好几个脸所以用回圈绘出来 for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) # 蓝色框住猫脸 cv2.imshow("Face", img) # 显示影像 cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_15.py 侦测单只猫脸
猫照片取材自英文版维基网站,图片作者是 Von.grzanka。
程式实例 ch27_16.py:侦测多数猫的影像
# ch27_16.py import cv2 pictPath = r'C:\opencv\data\haarcascade_frontalcatface.xml' cat_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("cat2.jpg") # 读取影像 faces = cat_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 9, minSize=(20,20)) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h),(255,0,0),2) cv2.imshow("Face", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_16.py 侦测多只猫脸
第 8 列设定 minNeighbors = 9,如果保持原先的 3,则会有非猫脸产生。
27-9

俄罗斯车牌辨识

在分类器资源档中与俄罗斯车牌分类有关的档案如下:

haarcascade_russian_plate_number.xml

笔者在测试过程发现台湾车牌几乎无法辨识,为了测试此车牌辨识,笔者自行参考不同格式的俄罗斯车牌设计了俄罗斯车牌,的确可以正常辨识。笔者将在第 30 章讲解自行设计哈尔(Haar)分类器档案,侦测台湾的车牌辨识。

程式实例 ch27_17.py:侦测台湾车牌,结果无法辨识
# ch27_17.py import cv2 pictPath = r'C:\opencv\data\haarcascade_russian_plate_number.xml' car_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("car.jpg") # 读取影像 plates = car_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) # 将车牌框起来,由于有可能找到好几个脸所以用回圈绘出来 for (x,y,w,h) in plates: cv2.rectangle(img, (x,y),(x+w,y+h),(255,0,0),2) # 蓝色框住车牌 cv2.imshow("Car Plate", img) # 显示影像 cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_17.py 与 ch27_18.py 车牌侦测对照
台湾车牌几乎无法辨识;改用俄罗斯车牌格式后可侦测到车牌区块。
程式实例 ch27_18.py:侦测俄罗斯车牌的应用
# ch27_18.py import cv2 pictPath = r'C:\opencv\data\haarcascade_russian_plate_number.xml' car_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("car1.jpg") # 读取影像 plates = car_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in plates: cv2.rectangle(img, (x,y),(x+w,y+h),(255,0,0),2) cv2.imshow("Car Plate", img) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch27_18.py 侦测俄罗斯车牌
成功读取车牌区块。
程式实例 ch27_19.py:更改另一种俄罗斯车牌格式
# ch27_19.py import cv2 pictPath = r'C:\opencv\data\haarcascade_russian_plate_number.xml' car_cascade = cv2.CascadeClassifier(pictPath) # 建立辨识物件 img = cv2.imread("car2.jpg") # 读取影像 plates = car_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors = 3, minSize=(20,20)) for (x,y,w,h) in plates: cv2.rectangle(img, (x,y),(x+w,y+h),(255,0,0),2) cv2.imshow("Car Plate", img) cv2.waitKey(0) cv2.destroyAllWindows()
习题

1. 请使用 g4.jpg 影像执行人脸辨识,你可能会获得有瑕疵的结果,请输出此影像。

习题一 人脸辨识示例

2. 请自行调整上述实例程式,让辨识成功。

3. 请使用 dogcat.jpg 影像辨识猫脸,请自行设计与调整参数,请调整到可以辨识 2 只猫脸。

习题三 猫脸辨识示例

4. 请调整上述范例可以辨识 4 个猫脸。

5. 整合 ch27_6_1.pych27_6_2.py,使用 s_1927.jpg 让图中所有科学家可以被辨识。

习题五 索维尔会议人脸辨识示例