前言
我先用了JAVA的opencv,发现opencv自带的人脸检测精度实在是不行。于是上网查了之后了解到dlib库是一个专门用来实现人脸检测和识别的库,精确度更高。只可惜只能部署在C++和聘用合同而JAVA没有,于是转攻python。
本文需要用到的检测人脸文件有:
shape_predictor_5_face_landmarks.dat
shape_predictor_68_face_landmarks.dat
dlib_face_recognition_resnet_model_v1.dat
haarcascade_frontalface_default.xml 请自行下载
一、部署
这里直接使用pip就行了,速度太慢的话考虑一下国内镜像或者找一下whl的网址下载下来部署。具体流程如下:
1.cmd,cd 含有whl文件的路径。
2.
pip install <package_name>.whl
就可以进入使用了。
二、使用
我们公司希望的使用场景是实时检测人脸,因此我想到的实现方案是由摄像头进行录像截取,截取出一段时长较短的录像,交给服务器处理,处理结束之后如果没有找到数据库中的嫌疑人人脸则安全,否则报警。
所以这里的第一步是将视频进行抽帧,间隔x秒截图一次,进行人脸检测。
1.
def extract_frames(video_path, output_path, frame_interval):
# 打开视频文件
video = cv2.VideoCapture(video_path)
if not video.isOpened():
print("无法打开视频文件")
return
# 获取视频的帧率和总帧数
fps = video.get(cv2.CAP_PROP_FPS)
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
print("帧率:", fps)
print("总帧数:", total_frames)
# 设置每隔几秒抽取一帧
interval = int(fps) * frame_interval
# 初始化计数器
count = 0
while True:
# 读取帧
ret, frame = video.read()
# 如果没有读取到帧或已经抽取到了指定数量的帧,则退出循环
if not ret or count >= total_frames:
break
# 抽取帧并保存
if count % interval == 0:
output_frame_path = output_path + "/frame_" + str(count) + ".jpg"
cv2.imwrite(output_frame_path, frame)
# print("保存帧:", output_frame_path)
count += 1
# 释放视频文件
video.release()
第二步就是加载处理器,记得使用绝对路径防止出现问题
2.
predictor68 = dlib.shape_predictor('D:/WorkPython/pythonProject/test2/shape_predictor_68_face_landmarks.dat')
predictor5 = dlib.shape_predictor('D:/WorkPython/pythonProject/test2/shape_predictor_5_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('D:/WorkPython/pythonProject/test2/dlib_face_recognition_resnet_model_v1.dat')
face_cascade = cv2.CascadeClassifier('D:/WorkPython/pythonProject/test2/haarcascade_frontalface_default.xml')
detector = dlib.get_frontal_face_detector()
第三步是用这里的5点特征点人脸处理器对检测出人脸的照片进行截图。因为我们这里的处理器对照片尺寸是有要求的。而这里使用5点而不是68点的原因在于速度,如果你的服务器能保证运行68点的处理器还能实时的话建议是使用68特征点的处理器。
3.
def reshape(image_path0, count, save_path, save_path_rgb):
global start_time3, start_time2, start_time1
k = 1
for i in range(1, count + 1):
image_path = image_path0 + "/face_"
img = image_path + str(i) + ".jpg"
image = cv2.imread(img)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
if len(faces) < 1:
continue
for (x, y, w, h) in faces:
# 裁剪人脸区域并调整大小为150x150像素
face_chip = cv2.resize(image[y:y + h, x:x + w], (150, 150))
image_rgb = cv2.cvtColor(face_chip, cv2.COLOR_BGR2RGB)
# 保存待处理图片和用于观看的图片
cv2.imwrite(save_path + "/face_" + str(k) + ".jpg", image_rgb)
cv2.imwrite(save_path_rgb + "/face_" + str(k) + ".jpg", face_chip)
k += 1
return k
由于特征点处理的人脸是bgr的,而我们最后希望得到的是rgb的,所以我这里设置了两个文件夹进行处理。
第四步则是通过opencv自带的检测器和5特征点处理器再次检测人脸。虽然这个检测器精准度不高,但是他识别快,能够有效的减少检测范围,防止检测时长过长导致无法进行实时监测。而5特征点处理器则是保证精确度的下限。
4.
def has_face(save_path, file_path, detector, predictor5, k):
for i in range(1, k):
image_path = file_path + "/face_" + str(i) + ".jpg"
image_path1 = save_path + "/face_" + str(i) + ".jpg"
# 加载图像
image = dlib.load_rgb_image(image_path)
# 使用人脸检测器检测人脸
faces = detector(image)
# 判断是否检测到人脸
if len(faces) > 0:
# 获取第一个人脸的关键点
shape = predictor5(image, faces[0])
# 判断关键点数量是否满足要求
if len(shape.parts()) < 5: # 使用五个关键点进行判断
# 设置需要修改的权限(给予删除权限)
new_permissions = stat.S_IWRITE
# 修改文件权限
os.chmod(image_path, new_permissions)
os.chmod(image_path1, new_permissions)
# 删除文件
os.remove(image_path)
os.remove(image_path1)
if len(faces) <= 0:
# 设置需要修改的权限(给予删除权限)
new_permissions = stat.S_IWRITE
# 修改文件权限
os.chmod(image_path, new_permissions)
os.chmod(image_path1, new_permissions)
# 删除文件
os.remove(image_path)
os.remove(image_path1)
这里对两个文件夹同时处理,确保最后能够返回rgb的人脸。
第五步则是将这里能够留下的人脸与我们的人脸库进行比较,留下可能是同一个人的人脸这里用的是68特征点以及特征点的相对位置来比较,有误检测的可能,不过由于是为了找到嫌疑人,因此误报是可以接受的,但是漏报是绝对不允许的。
5.
def extract_face_landmarks(image, predictor68, detector):
# 将图像转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用人脸检测器检测图像中的人脸
faces = detector(gray)
# 保存特征点
landmarks = []
# 提取特征点
for face in faces:
# 使用关键点检测器检测人脸关键点
face_landmarks = predictor68(gray, face)
# 将关键点坐标保存到列表中
for i in range(68):
x = face_landmarks.part(i).x
y = face_landmarks.part(i).y
landmarks.append((x, y))
return landmarks
def compare_image_quality(landmarks, image2, predictor68, detector, img2, img2_1):
print(" ")
# 提取图像2的特征点
landmarks2 = extract_face_landmarks(image2, predictor68, detector)
ii = []
i = 1
k = 0
if len(landmarks2) == 0:
# 设置需要修改的权限(给予删除权限)
new_permissions = stat.S_IWRITE
# 修改文件权限
os.chmod(img2, new_permissions)
os.chmod(img2_1, new_permissions)
# 删除文件
os.remove(img2)
os.remove(img2_1)
ii.append(k)
return ii
for landmark in landmarks:
# 比较特征点的差异
diff = 0
for (x1, y1), (x2, y2) in zip(landmark, landmarks2):
diff += (x1 - x2) ** 2 + (y1 - y2) ** 2
# 计算平均差异值
avg_diff = diff / len(landmarks2)
if avg_diff <= 180:
k = i
print(avg_diff)
ii.append(k)
i += 1
if k == 0:
# 设置需要修改的权限(给予删除权限)
new_permissions = stat.S_IWRITE
# 修改文件权限
os.chmod(img2, new_permissions)
os.chmod(img2_1, new_permissions)
# 删除文件
os.remove(img2)
os.remove(img2_1)
return ii
def retect(count, save_path, landmarks, predictor68, detector):
arr = []
for i in range(1, count + 1):
img2 = save_path + "_rgb/face_" + str(i) + ".jpg"
img2_1 = save_path + "/face_" + str(i) + ".jpg"
image2 = np.array(Image.open(img2))
l = compare_image_quality(landmarks, image2, predictor68, detector, img2, img2_1)
for k in l:
if k != 0:
arr.append([i, k])
return arr
# 再次检测,删除误检测;检测可能是同一个人脸的照片
find_path = "D:/WorkSpace/video/src/find"
count = rename(find_path)
landmarks = []
for i in range(1, count + 1):
img = find_path + "/face_" + str(i) + ".jpg"
image = np.array(Image.open(img))
landmark = extract_face_landmarks(image, predictor68, detector)
landmarks.append(landmark)
# 整理文件夹
rename(save_path)
count = rename(save_path_rgb)
arr = retect(count, save_path, landmarks, predictor68, detector)
这里的rename方法是对文件夹里的文件重命名为有顺序结构的jpg,并且返回文件个数用于整理文件夹,不太重要,就不放上来了。
需要注意的是嫌疑人人脸库的landmarks也就是特征点坐标的列表要提前加载好而不能放到方法里,不然会多次运算,显著降低运行效率。
第六步也就是最后一步,我们把所有可能是嫌疑人的照片进行详细对比。这里用的是计算欧氏距离的方法,相对于其他的范数运算,这个方法精确度是可以接受的,而且速度较快,如果一张图人脸不超过20个,应该是可以达到每秒检测一次的,所以我这里选择了他。
6.
def distan(face_chip1, face_chip2):
face_descriptor1 = facerec.compute_face_descriptor(face_chip1)
face_descriptor2 = facerec.compute_face_descriptor(face_chip2)
# 计算欧氏距离
desc1 = np.array(face_descriptor1)
desc2 = np.array(face_descriptor2)
euclidean_distance = np.linalg.norm(desc1 - desc2)
print("欧氏距离 ", euclidean_distance)
if euclidean_distance < 0.48:
return True
else:
return False
def face_detect(save_path, arr, find_path):
while len(arr) != 0:
[i, k] = arr[0]
img1 = find_path + "/face_" + str(k) + ".jpg"
face_chip1 = cv2.imread(img1)
img2 = save_path + "_rgb/face_" + str(i) + ".jpg"
img2_1 = save_path + "/face_" + str(i) + ".jpg"
face_chip2 = cv2.imread(img2)
same_person = distan(face_chip1, face_chip2)
if same_person:
destination_folder_rgb = "D:/WorkSpace/video/src/same" + str(k)
count = rename(destination_folder_rgb)
file_name1 = "/face_" + str(i) + ".jpg"
file_name2 = "/face_" + str(count+1) + ".jpg"
source_path_rgb = save_path + "_rgb"
arr = [x for x in arr if not (x[0] == i)]
print(arr)
try:
move_images(source_path_rgb, destination_folder_rgb, file_name1, file_name2)
except FileNotFoundError:
print(" ")
else:
arr.remove([i, k])
ar = [x for x in arr if (x[0] == i)]
if len(ar) == 0:
# 设置需要修改的权限(给予删除权限)
new_permissions = stat.S_IWRITE
# 修改文件权限
os.chmod(img2, new_permissions)
os.chmod(img2_1, new_permissions)
# 删除文件
os.remove(img2)
os.remove(img2_1)
# 检测是否同一个人(一张图1秒左右)
face_detect(save_path, arr, find_path)
以上就是整个人脸比对和检测的流程了,希望对你有所帮助。