Python人脸检测及识别

Python人脸检测及识别

开发环境:

  1. Ubuntu16.04
  2. Python3.6 / Tensorflow1.8

项目目标:

  1. 实时识别检测人脸,并识别人物;

项目思路:

  • 使用mtcnn模型检测人脸,并提取人脸图像成对应数据集;
  • 使用facenet模型对数据集中每张人脸进行特征提取,构建每张图片对应128d维数据特征;
  • 建立人脸特征库;
  • 对待识别人脸,进行1、 2步骤,将提取的特征与人脸特征库进行相似度计算,找到相似性较高的对应人脸;

具体实施:

  1. opencv人像采集:
    使用opencv进行人像采集,其中使用ssd人脸检测器(快且准),详细代码如下get_face.py

    import os
    import cv2
    import numpy as np
    from cv2_face_detector import FaceDetector
    
    # OpenCv 调用摄像头 use camera
    cap = cv2.VideoCapture(0)
    cnt_ss = 0
    # 存储人脸的文件夹
    current_face_dir = ""
    
    # 保存 faces images 的路径
    path_photos_from_camera = "./faces_from_camera/"
    # 新建保存人脸图像文件
    def pre_work_mkdir():
        if os.path.isdir(path_photos_from_camera):
            pass
        else:
            os.mkdir(path_photos_from_camera)
    pre_work_mkdir()
    
    # 如果有之前录入的人脸 
    # 在之前 person_x 的序号按照 person_x+1 开始录入
    if os.listdir("./faces_from_camera/"):
        # 获取已录入的最后一个人脸序号 / get the num of latest person
        person_list = os.listdir("./faces_from_camera/")
        person_num_list = []
        for person in person_list:
            person_num_list.append(int(person.split('_')[-1]))
        person_cnt = max(person_num_list)
    # 如果第一次存储或者没有之前录入的人脸, 按照 person_1 开始录入
    else:
        person_cnt = 0
    
    # 之后用来控制是否保存图像的 flag
    save_flag = 1
    # 之后用来检查是否先按 'n' 再按 's'
    press_n_flag = 0
    # 使用ssd 人脸检测器
    faceDetector = FaceDetector('ssd', 0.5)
    # 字体类型
    font = cv2.FONT_HERSHEY_COMPLEX
    while cap.isOpened():
        flag, img_rd = cap.read()
        kk = cv2.waitKey(1)
        frame = img_rd.copy()
        faces = faceDetector.detect(frame)
        # 固定截取窗口
        (x,y,w,h) = (150,100,250,250)
        cv2.rectangle(frame, (x, y),(x+w, y+h),
                        (0, 0, 255) , 2)
    
        # press 'n' to create the folders for saving faces
        if kk == ord('n'):
            person_cnt += 1
            current_face_dir = path_photos_from_camera + "person_" + str(person_cnt)
            os.makedirs(current_face_dir)
            print('\n')
            print("新建的人脸文件夹 / Create folders: ", current_face_dir)
            cnt_ss = 0              # clear the cnt of faces
            press_n_flag = 1        # have pressed 'n'
    
       # 检测到人脸
        if len(faces) != 0:
            print(len(faces))
            for rect in faces:
                (x, y, w, h) = rect
                cv2.rectangle(frame, (x, y),(x+w, y+h),
                                (0, 255, 0) , 2)
                if save_flag:
                    # press 's' to save faces into local images
                    if kk == ord('s'):
                        # check if you have pressed 'n'
                        if press_n_flag:
                            cnt_ss += 1
                            # 保存固定窗口截图
                            im_blank = img_rd[100:350,150:400,:]
                            cv2.imwrite(current_face_dir + '/' +str("%03d" % cnt_ss) + ".jpg", im_blank)
                            print("写入本地 / Save into:", str(current_face_dir)+ '/' + str("%03d" % cnt_ss) + ".jpg")
                        else:
                            print("Please press 'N' before 'S'")
    
        # # 添加说明 / add some statements
        cv2.putText(frame, "Face Register", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA)
        cv2.putText(frame, "N: New face folder", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
        cv2.putText(frame, "S: Save current face", (20, 400), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
        cv2.putText(frame, "Q: Quit", (20, 450), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
    
        # show the numbers of faces detected
        cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
        # press 'q' to exit
        if kk == ord('q'):
            break
        # 如果需要摄像头窗口大小可调
        cv2.namedWindow("camera", 0)
        cv2.imshow("camera", frame)
    
    # 释放摄像头 / release camera
    cap.release()
    cv2.destroyAllWindows()
    

    人脸采集过程截图:Python人脸检测及识别_第1张图片

  2. facenet提取人脸特征:

    • mtcnn检测人脸,剪裁有效人脸区域

    • facene提取人脸特征
      详细代码如下face_feature.py

      import cv2
      import csv
      from os.path import join as pjoin
      import skimage
      import tensorflow as tf
      import numpy as np
      import os
      import facenet
      import align.detect_face
      
      minsize = 20 # minimum size of face
      threshold = [ 0.6, 0.7, 0.7 ]  # three steps's threshold
      factor = 0.709 # scale factor
      
      # 创建mtcnn网络,并加载参数
      print('Creating networks and loading parameters')
      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))
          with sess.as_default():
              pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
      
      def load_and_align_data(image, image_size, margin, gpu_memory_fraction):
      
          # 读取图片 
          img = image
          # 获取图片的shape
          img_size = np.asarray(img.shape)[0:2]
          # 返回边界框数组 (参数分别是输入图片 脸部最小尺寸 三个网络 阈值 factor不清楚)
          bounding_boxes, _ = align.detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor)
      
          # 如果检测出图片中不存在人脸 则直接返回,return 0(表示不存在人脸,跳过此图)
          if len(bounding_boxes) < 1:
              return 0,0,0
          else:
              crop=[]
              det=bounding_boxes
              det[:,0]=np.maximum(det[:,0], 0)
              det[:,1]=np.maximum(det[:,1], 0)
              det[:,2]=np.minimum(det[:,2], img_size[1])
              det[:,3]=np.minimum(det[:,3], img_size[0])
              det=det.astype(int)
      
              for i in range(len(bounding_boxes)):
                  temp_crop=img[det[i,1]:det[i,3],det[i,0]:det[i,2],:]
                  aligned=skimage.transform.resize(temp_crop, (image_size, image_size))
                  prewhitened = facenet.prewhiten(aligned)
                  crop.append(prewhitened)
              crop_image=np.stack(crop)    
              return det,crop_image,1
      
      def to_rgb(img):
          w, h = img.shape
          ret = np.empty((w, h, 3), dtype=np.uint8)
          ret[:, :, 0] = ret[:, :, 1] = ret[:, :, 2] = img
          return ret
      
      def load_data(data_dir):
          # data为字典类型 key对应人物分类 value为读取的一个人的所有图片 类型为ndarray
          data = {}
          pics_ctr = 0
          for guy in os.listdir(data_dir):
              person_dir = pjoin(data_dir, guy)       
              curr_pics = [read_img(person_dir, f) for f in os.listdir(person_dir)]         
      
              # 存储每一类人的文件夹内所有图片
              data[guy] = curr_pics      
          return data
      
      def read_img(person_dir,f):
          img=cv2.imread(pjoin(person_dir, f))
          gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)      
          # 判断数组维度
          if gray.ndim == 2:
              img = to_rgb(gray)
          return img
      
      # 模型位置
      model_dir='./models/facenet/'
      with tf.Graph().as_default():
          with tf.Session() as sess:
              # 加载facenet模型
              facenet.load_model(model_dir)
              # 返回给定名称的tensor
              images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
              embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
              phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
              # 从训练数据文件夹中加载图片并剪裁,最后embding,data为dict
              data=load_data('./train_data/')	
              # keys列表存储图片文件夹类别(几个人)
              keys=[]
              for key in data:
                  keys.append(key)
                  print('folder:{},image numbers:{}'.format(key,len(data[key])))
              # 使用mtcnn模型获取每张图中face的数量以及位置,并将得到的embedding数据存储
              for n in range(len(keys)):
                  for x in data[keys[n]]:
                      _,images_me,i = load_and_align_data(x, 160, 44, 1.0)
                      if i:
                          feed_dict = { images_placeholder: images_me, phase_train_placeholder:False }
                          emb = sess.run(embeddings, feed_dict=feed_dict) 
                          for xx in range(len(emb)):
                              emb=list(emb[xx,:])
                              emb.append(keys[n])
                              with open('face_feature.csv', "a+", newline="") as csvfile:
                                  writer = csv.writer(csvfile)
                                  writer.writerow(emb)
      
      
    1. 人脸特征库建立
      上述步骤生成文件face_feature.csv即为已经采集的人脸特征库;

    2. 效果验证
      在数据集处理过程中,已经将数据集分为训练集和测试集,比例约为8:2,经过验证比对,平均准确率达到百分之95以上,具体每个类别识别准确率如下:

      name==  yin123
      acc: 1.0
      name==  liu123
      acc: 0.9855072463768116
      name==  yu123
      acc: 1.0
      name==  ma123
      acc: 0.9821428571428571
      name==  zhou123
      acc: 0.9912280701754386
      name==  wang123
      acc: 1.0
      name==  xu123
      acc: 1.0
      name==  taojinglong
      acc: 0.8529411764705882
      
    3. 实时检测
      设备描述:8g内存+i7+gtx960m+自带摄像头
      详细代码如face_recognition.py
      检测效果如图:
      Python人脸检测及识别_第2张图片

    4. 备注

      • 文中使用模型,可至此下载
      • 经过实际效果测试:延迟尚能接受,准确度良好(采集设备比较差)。完整项目参见,有问题直接issuse.

你可能感兴趣的:(图像处理,facenet)