本文介绍如何使用基于 Python 语言,使用 OpenCV 探测图片上的人脸,并把人脸图片裁切保存。
想法是这样的:识别一张图片(jpg或png格式)中的人脸,并把眼睛标记出来,如果识别出来了人脸了,就把这些人脸按照被发现的顺序,裁切保存为原图格式的小图。
环境
Windows 10+Python 3.x
准备
安装几个需要的 Python 库/包:
1 2 | pip install opencv-python pip install Pillow |
第一个包用于人脸识别,第二个包用于把识别出来的人脸裁切并保存。
创建一个目录,最好是英文名称,比如 Face-Detection,在这个目录下创建个叫xml 的目录,里面放两个文件:
1 2 3 | https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_eye.xml |
回到 Face-Detection 目录,使用搜索引擎,搜索"合影",尽量选几张稍微大些的图,重命名为好输入的名字,比如 a.jpg, b.png。
代码
在 Face-Detection 目录下创建一个名字为 fb.py (Face Detection )的文件。把下面的代码粘贴进这个文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | # -*- coding: utf-8 -*- # @Author: suifengtec # @Date: 2018-09-29 11:28:33 # @Last Modified by: suifengtec # @Last Modified time: 2018-09-29 12:31:30 import cv2 import sys import os from PIL import Image # Face Detection # 从jpg或png格式的图片中探测人脸,并裁切为相应格式的人脸小图,放在crops目录中。 # # # 使用方法: # python fd.py [当前目录下的图片名称] [如果想直接看到标记好的大图,在这儿随意添加一个参数即可] # 使用示例: # python fd.py c.png # python fd.py c.jpg # python fd.py c.jpg 1 def isSupporttedImgExt(imgName): portion = os.path.splitext(imgName) if portion[1] == '.jpg': return '.jpg' elif portion[1] == '.JPG': return '.JPG' elif portion[1] == '.JPEG': return '.JPEG' elif portion[1] == '.png': return '.png' elif portion[1] == '.PNG': return '.PNG' else: return False # 用于截取图上的人脸,并把裁切所得的相应格式的小图,按照人脸被发现的顺序,放在crops目录中 # 如原图名称为 c.png,如果发现有5张人脸,将会生成c_X.png,x等于1,2,3,4,5 # 这五张小图放在工作目录下的crops目录中。 def cutImg(thisDir, imgName, id, x, y, w, h): imgPath = os.path.join(thisDir, imgName) if os.path.exists(imgPath) != True: print('cutImg:没找到要被裁切文件.') return ext = isSupporttedImgExt(imgName) name = imgName.replace(ext, '_'+str(id)+ext) # 裁切后的目录名称,它在thisDir下面。 cropImgDirName = 'crops' cropImgDir = os.path.join(thisDir, cropImgDirName) cropedImgName = os.path.join(cropImgDir, name) # 存在同名的文件,目前的策略是,返回吧,不再生成,生产中,应考虑这一点,这只是测试... if os.path.exists(cropedImgName) == True: return if os.path.exists(cropImgDir) != True: os.mkdir(cropImgDir) elif os.path.isdir(cropImgDir) != True: os.mkdir(cropImgDir) # 打开特定图片,裁切后保存 Image.open(imgPath).crop((x, y, (x+w), (y+h))).save(cropedImgName) def main(args): # 获取用户从命令行输入的参数 imgName = args[0] if isSupporttedImgExt(imgName) == False: print('不支持这种文件!') return 0 thisDir = os.getcwd() imgPath = os.path.join(thisDir, imgName) if os.path.exists(imgPath) != True: print('main:没找到要探测的文件.') return cascDir = os.path.join(thisDir, "xml") cascFaceFile = os.path.join(cascDir, "haarcascade_frontalface_default.xml") cascEyeFile = os.path.join(cascDir, "haarcascade_eye.xml") if os.path.exists(cascFaceFile) != True: print('main:没找到cascFile.') return if os.path.exists(cascEyeFile) != True: print('main:没找到cascEyeFile.') return faceCascade = cv2.CascadeClassifier(cascFaceFile) eyeCascade = cv2.CascadeClassifier(cascEyeFile) # CV2读图 img = cv2.imread(imgPath) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测图上的人脸数量 faces = faceCascade.detectMultiScale(gray, 1.3, 5, minSize=(30, 30)) print("找到{0} 张人脸!".format(len(faces))) # 给发现的人脸,画一个绿色的方框,并打印出相应的坐标和宽高,然后裁切保存。 detectedId = 0 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eyeCascade.detectMultiScale(roi_gray, 1.1, 5, minSize=(6, 6)) for (ex, ey, ew, eh) in eyes: cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2) print("x={0},y={1},dx={2},dy={3}".format(x, y, w, h)) detectedId = detectedId+1 cutImg(thisDir, imgName, detectedId, x, y, w, h) if len(args) > 1: cv2.imshow("Face Detect: {0}".format(len(faces)), img) c = cv2.waitKey(0) if 'q' == chr(c & 255): cv2.destroyAllWindows() else: # cv2.waitKey(0) cv2.destroyAllWindows() if __name__ == '__main__': main(sys.argv[1:]) |
现在,Face-Detection 目录里,应该有这些文件了:
测试
假如你的 Face-Detection 目录中已经有个叫 c.jpg 的某些人的合影,那就可以在Face-Detection 目录中使用下面任意一条命令来进行人脸探测了:
1 2 | python fd.py c.jpg python fd.py c.jpg 1 |
如上所示,在图片名称后面任意加一个参数的话,会看到 Python 使用 OpenCV 标记好的大图,如果没有这个附加参数的话,那就不会看到这个标记好的大图。
有没有额外参数,都会把探测到的人脸,按照发现的顺序,裁切后保存在 Face-Detection 目录下的 crops 目录中,如下所示:
上图中的人像来自搜索引擎搜索出来的一张合影照片,如侵权,请凭证联系我。
加上额外参数后,会看到标记好人脸和眼睛的大图,如下所示(所示为部分截图):
人脸的识别还是准确的,眼睛的识别,不是太准确,不过关系不是太大了。