前言

本文主要是记录项目过程,主要的流程如下:
1.传入视频流使用mtcnn人脸检测,将人脸区域裁剪出来并且保存。
2.利用拉普拉斯算子去掉保存的图片中模糊的图片。
3.利用dlib库中人脸特征提取的算法提取人脸特征,并且利用聚类去除重复人脸。

准备工作

Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。 Conda 是为 Python 程序创建的,适用于 Linux,OS X 和Windows,也可以打包和分发其他软件。

1.利用conda安装相关库

pip install opencv-python
pip install facenet_recogonition#这里是pip社区中人脸识别库,里面自带dlib库
pip install tensorflow-gpu==1.15.0#我使用的版本
pip install numpy==1.16.2#这边我使用高版本的会报错所以使用这个
pip install pillow
#其他缺什么就下什么

2.在git上下载facenet

git clone https://github.com/davidsandberg/facenet

3.项目结构

项目结构align:这个文件夹是从facenet中拷贝的,https://github.com/davidsandberg/facenet/tree/master/src/align,主要是MTCNN人脸检测的相关文件
dataset:这个文件夹是用来存放图片,视频相关文件。
model:存放了dlib提取特征的模型。

1.第一阶段(mtcnn检测人脸)

人脸检测方法很多,如Dilb,OpenCV,OpenFace人脸检测等等,这里使用MTCNN进行人脸检测,一方面是因为其检测精度确实不错,另一方面facenet工程中,已经提供了用于人脸检测的mtcnn接口。 MTCNN是多任务级联CNN的人脸检测深度学习模型,该模型中综合考虑了人脸边框回归和面部关键点检测。它的参数模型也保存在align文件夹下,分别是det1.npy,det2.npy,det3.npy

1.1 捕捉视频流

首先,利用文件夹中Video_capture.py用来录制一段视频(包含人脸)保存至dataset/output文件夹中。

import cv2
cap = cv2.VideoCapture(0)  # 打开相机
# 创建VideoWriter类对象
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('./dataset/video/output.avi', fourcc, 20  , (640, 480))
while (True):
    ret, frame = cap.read()  # 捕获一帧图像
    out.write(frame)  # 保存帧
    cv2.imshow('frame', frame)  # 显示帧
    # 判断按键,如果按键为q,退出循环
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()  # 关闭相机
out.release()
cv2.destroyAllWindows()  # 关闭窗口

1.2 将视频文件转换为图片格式

#Face_rect.py文件中
def Video_capture(video):
    i=1
    k=1
    #打开摄像头||打开视频
    cap = cv2.VideoCapture(video)
    #视频帧计数间隔频率
    timeF =20
    while (True):
        # 读取摄像头的每一帧图片
        ret, frame = cap.read()
        # 显示摄像头
        #cv2.imshow('frame', frame)
        if ret:
            #保存成jpg格式
            if(i%timeF==0):
                cv2.imwrite("./dataset/images/" + str(k) + ".jpg", frame)
                k=k+1
            i = i+1
        else:
            break
    # 当处理完成释放摄像头捕获
    cap.release()
    cv2.destroyAllWindows()

1.3 检测人脸并且裁剪出人脸区域

人脸检测使用mtcnn检测,也可以使用face_regoconition中的HOG或者CNN。

#Face_rect.py文件中
def detection_face(path):
    # 这里引用facenet/src/align/align_dataset_mtcnn.py文件的代码对采集帧进行人脸检测和对齐
    minsize = 20  # minimum size of face
    threshold = [0.6, 0.7, 0.7]  # three steps's threshold
    factor = 0.709  # scale factor

    with tf.Graph().as_default():
        gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.5)
        sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
        #sess = tf.Session()
        with sess.as_default():
            pnet, rnet, onet = detect_face.create_mtcnn(sess, None)

    image_path = gb.glob(path)

    j=1

    for path in image_path:
        # 读取images文件夹中的图片
        image = face_recognition.load_image_file(path)

        # 使用默认的HOG模型找到所有包含脸的图片
        #face_locations = face_recognition.face_locations(image)

        # 使用cnn
        #face_locations = face_recognition.face_locations(image, number_of_times_to_upsample=0, model="cnn")

        #使用align中mtcnn检测人脸

        face_locations,_ = detect_face.detect_face(image,minsize,pnet,rnet,onet,threshold, factor)

        print("I found {} face(s) in this photograph.".format(len(face_locations)))

        for face_location in face_locations:

            # 显示每一张脸在图片中的位置
            top = int(face_location[1])
            right = int(face_location[2])
            bottom = int(face_location[3])
            left = int(face_location[0])

            #top, right, bottom, left = face_location

            #print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))
            #更改人脸框的位置
            h=(bottom-top)//4
            w=(right-left)//4
            try:
            # 显示找到的人脸图片
                face_image = image[(top-h):(bottom+h), (left-w):(right+w)]
                pil_image = Image.fromarray(face_image)
            except Exception as e:
                print("could not save:",e)
            # pil_image.show()
            # 保存识别出来的人脸图片
            pil_image.save("./dataset/images_result/" + "%f"%time.time() + ".jpg")
        j=j+1

2. 第二阶段(去模糊图片)

有两种思路:
1.首先对输入的图片进行快速傅里叶变换操作;然后检测图片中高频分量和低频分量的分布情况;接着根据图片中包含的高频分量的多少进行合适的判断,如果图片中含有较少的高频分量,则表明该图片是模糊图片,否则是清晰图片。
2.即根据Laplacian变换的方差值进行判断。
这里选择第二种方式:

#image_process.py文件中
def variance_of_laplacian(image):
    '''
    计算图像的laplacian响应的方差值
    '''
    return cv2.Laplacian(image, cv2.CV_64F).var()

def compute_blurry(image_path,threshold):

    i=0
    path=gb.glob(image_path)
    # 遍历每一张图片
    for imagePath in path:
        # 读取图片
        image = cv2.imread(imagePath)
        # 将图片转换为灰度图片
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        # 计算灰度图片的方差
        fm = image_process.variance_of_laplacian(gray)
        # 设置输出的文字
        if fm < threshold:
            os.remove(imagePath)
            i=i+1
        # 显示结果
    print(" Deleted blurry images:",i,"s")

第三阶段人脸聚类

人脸聚类使用了dlib的人脸检测和特征点检测

import sys
import os
import dlib
import glob
import cv2
import time

# 指定路径
current_path = os.getcwd()
model_path = current_path + '/models/'
shape_predictor_model = model_path + '/shape_predictor_5_face_landmarks.dat'
face_rec_model = model_path + '/dlib_face_recognition_resnet_model_v1.dat'
face_folder = current_path + '/dataset/images_result/'
output_folder = current_path + '/dataset/output/'

# 导入模型
detector = dlib.get_frontal_face_detector()
shape_detector = dlib.shape_predictor(shape_predictor_model)
face_recognizer = dlib.face_recognition_model_v1(face_rec_model)

# 为后面操作方便,建了几个列表
descriptors = []
images = []
# 遍历faces文件夹中所有的图片
for f in glob.glob(os.path.join(face_folder, "*.jpg")):
    print('Processing file:{}'.format(f))
    # 读取图片
    img = cv2.imread(f)
    # 转换到rgb颜色空间
    img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # 检测人脸
    dets = detector(img2, 1)
    print("Number of faces detected: {}".format(len(dets)))

    # 遍历所有的人脸
    for index, face in enumerate(dets):
        # 检测人脸特征点
        shape = shape_detector(img2, face)
        # 投影到128D
        face_descriptor = face_recognizer.compute_face_descriptor(img2, shape)

        # 保存相关信息
        descriptors.append(face_descriptor)
        images.append((img2, shape))

# 聚类
labels = dlib.chinese_whispers_clustering(descriptors, 0.5)
print("labels: {}".format(labels))
num_classes = len(set(labels))
print("Number of clusters: {}".format(num_classes))

# 为了方便操作,用字典类型保存
face_dict = {}
for i in range(num_classes):
    face_dict[i] = []
# print face_dict
for i in range(len(labels)):
    face_dict[labels[i]].append(images[i])

#print (face_dict.keys())
# 遍历字典,保存结果
for key in face_dict.keys():
    file_dir = os.path.join(output_folder)
    if not os.path.isdir(file_dir):
        os.makedirs(file_dir)
    for index, (image, shape) in enumerate(face_dict[key]):
        if index==0:
            #file_path = os.path.join(file_dir, 'face_' + str(index))
            #print (file_path)
            dlib.save_face_chip(image, shape, file_dir+"%f"%time.time(), size=150, padding=0.25)

使用该方法发现效果不是特别理想,最终采用求取特征值然后计算每两张人脸之间的距离删除小于0.5的

#image_process.py文件中
def detect_faces_in_image(file_stream1, file_stream2):
    try:
        #提取图片中人脸的特征值
        code1 = face_recognition.face_encodings(face_recognition.load_image_file(file_stream1))[0]
        #print(code1)
        code2 = face_recognition.face_encodings(face_recognition.load_image_file(file_stream2))[0]
        #比较人脸特征值
        if len(code1) > 0 and len(code2) > 0:
            match_results = face_recognition.face_distance([code1], code2)
            score=match_results[0]
        return score
    except Exception as d:
        print("face detection error :",d)


def repeat_remove(image_path):
    path=gb.glob(image_path)
    imagepath=[]
    l=0
    for i in path:
        imagepath.append(i)
    try:
       for j in range(0,len(imagepath)):
           for k in range(j+1,len(imagepath)):
               score=detect_faces_in_image(imagepath[j],imagepath[k])
               try:
                   if score < 0.5:
                       os.remove(imagepath[k])
                       l=l+1
                       print(imagepath)
               except Exception as c:
                    print("not a number :",c)
       print("delete repetitive images:" ,l)
    except Exception as e:
        print("error :",e)

第四阶段测试

if __name__=="__main__":
videos = gb.glob("./dataset/video/*.*")  # 得到文件夹下的所有文件名称
try:
    for file in videos:
        Video_capture(file)
        detection_face("./dataset/images/*.jpg")
        image_process.image_process.compute_blurry("./dataset/images_result/*.jpg",100)
        #image_process.repeat_remove("./dataset/images_result/*.jpg")
        #move_file("./dataset/images_result/", "./dataset/save/")
        #os.remove(file)
except  Exception as e:
    print("No Videa file:",e)
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐