python实现对视频中的人脸检测并与人脸库中比对

前言

我先用了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)

以上就是整个人脸比对和检测的流程了,希望对你有所帮助。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值