人脸关键点检测和头部姿态估计数据集生成

接上篇 人脸关键点检测和头部姿态估计数据集整理

1. 借助于OpenPose生成人脸关键点和头部姿态,使用链接:https://github.com/TadasBaltrusaitis/OpenFace/wiki/Command-line-arguments

build/bin/FaceLandmarkImg.exe -fdir "images path"

具体使用可参考博客,每张人脸可得到一个csv文件,里面记录了人脸的很多信息,我们只需要68个关键点和头部姿态

2. 借助于人脸检测网络生成人脸检测结果,每张人脸可得到一个csv文件,里面记录了人脸的左上角和右下角坐标,具体使用可参考博客

3.结合OpenPose和人脸检测结果生成数据集label标注文件,格式为:

face_path x1 y1 x2 y2 x3 y3 ...... x68 y68 pitch yaw roll

执行代码:Generate_labels.py

import sys, os
import cv2
import numpy as np
import csv
import argparse
import math
from shutil import copyfile

#Determine whether it is a rotation matrix
def isRotationMatrix(R):
    Rt = np.transpose(R)
    shouldBeIdentity = np.dot(Rt, R)
    I = np.identity(3, dtype=R.dtype)
    n = np.linalg.norm(I - shouldBeIdentity)
    return n < 1e-6

#rotationvector to rorationmatrix
def vec2matrix(rvec):
    theta = np.linalg.norm(rvec)
    r = rvec / theta
    R_ = np.array([[0, -r[2][0], r[1][0]],
               [r[2][0], 0, -r[0][0]],
               [-r[1][0], r[0][0], 0]])
    R = np.cos(theta) * np.eye(3) + (1 - np.cos(theta)) * r * r.T + np.sin(theta) * R_
    return R

#rotationmatrix to EulerAngles
def rotationMatrixToAngles(R):
    assert (isRotationMatrix(R))
    sy = math.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
    singular = sy < 1e-6
    if not singular:
        x = math.atan2(R[2, 1], R[2, 2])
        y = math.atan2(-R[2, 0], sy)
        z = math.atan2(R[1, 0], R[0, 0])
    else:
        x = math.atan2(-R[1, 2], R[1, 1])
        y = math.atan2(-R[2, 0], sy)
        z = 0

    x = x*180.0/3.141592653589793
    y = y*180.0/3.141592653589793
    z = z*180.0/3.141592653589793
    return np.array([-1.0*x+10, y, z])

def compute_iou(rec1, rec2):
    """
    computing IoU
    :param rec1: (y0, x0, y1, x1), which reflects
            (top, left, bottom, right)
    :param rec2: (y0, x0, y1, x1)
    :return: scala value of IoU
    """
    # computing area of each rectangles
    S_rec1 = (rec1[2] - rec1[0]) * (rec1[3] - rec1[1])
    S_rec2 = (rec2[2] - rec2[0]) * (rec2[3] - rec2[1])

    # computing the sum_area
    sum_area = S_rec1 + S_rec2

    # find the each edge of intersect rectangle
    left_line = max(rec1[1], rec2[1])
    right_line = min(rec1[3], rec2[3])
    top_line = max(rec1[0], rec2[0])
    bottom_line = min(rec1[2], rec2[2])

    # judge if there is an intersect
    if left_line >= right_line or top_line >= bottom_line:
        return 0
    else:
        intersect = (right_line - left_line) * (bottom_line - top_line)
        return (intersect / (sum_area - intersect)) * 1.0


def Generate_labels(img_path, csv_path, txt_path, face_path, label_path):
    """

    :param img_path:
    :param csv_path:
    :param txt_path:
    :param face_path:
    :param label_path:
    :return:
    """
    img_list = os.listdir(img_path)
    fp_label = open(os.path.join(label_path, '300w_label.txt'), 'w')
    for img_file in img_list:
        print('now is dealing with '+str(img_file))
        # Iterative execution of each image
        filename, extension = os.path.splitext(img_file)
        extension = extension[1:]
        if extension == 'jpg' or extension == 'png':
            img = cv2.imread(os.path.join(img_path, img_file))
            # read and organize the result of OpenFace, result is saved to 'csv_result' for format as:
            # [
            # [point0_x,point0_y],
            # [point1_x,point1_y],
            # [point2_x,point2_y],
            # ......
            # [pose_x,pose_y,pose_z],
            # [xmin_csv,ymin_csv,xmax_csv,ymax_csv]
            # ]
            if not os.path.exists(os.path.join(csv_path, filename + '.csv')):
                continue
            csv_file = open(os.path.join(csv_path, filename + '.csv'), 'r')
            reader = csv.reader(csv_file)
            csv_result = []
            for item in reader:
                csv_result_temp = []
                xmin_csv = 50000
                ymin_csv = 50000
                xmax_csv = 0
                ymax_csv = 0
                if reader.line_num == 1:
                    continue
                for i in range(68):
                    csv_result_temp.append([float(item[296 + i]), float(item[296 + i + 68])])
                    xmin_csv = min(xmin_csv, float(item[296 + i]))
                    xmax_csv = max(xmax_csv, float(item[296 + i]))
                    ymin_csv = min(ymin_csv, float(item[296 + i + 68]))
                    ymax_csv = max(ymax_csv, float(item[296 + i + 68]))
                csv_result_temp.append([item[293], item[294], item[295]])
                csv_result_temp.append([xmin_csv, ymin_csv, xmax_csv, ymax_csv])
                csv_result.append(csv_result_temp)
            csv_file.close()
            # read and organize the label of 300w dataset, result is saved to 'pts_result' for format as:
            # [
            # [point0_x,point0_y],
            # [point1_x,point1_y],
            # [point2_x,point2_y],
            # ......
            # [xmin_pts,ymin_pts,xmax_pts,ymax_pts]
            # ]
            fp_pts = open(os.path.join(img_path, filename + '.pts'), 'r')
            lines = fp_pts.readlines()
            lines = lines[3:71]
            xmin_pts = 50000
            ymin_pts = 50000
            xmax_pts = 0
            ymax_pts = 0
            pts_result = []
            for line in lines:
                S = line.split(' ')
                point_x = float(S[0])
                point_y = float(S[1])
                xmin_pts = min(xmin_pts, point_x)
                ymin_pts = min(ymin_pts, point_y)
                xmax_pts = max(xmax_pts, point_x)
                ymax_pts = max(ymax_pts, point_y)
                pts_result.append([point_x, point_y])
            pts_result.append([xmin_pts, ymin_pts, xmax_pts, ymax_pts])
            fp_pts.close()
            face_pose = []
            face_bbox = []
            iou_max = 0
            for i in range(len(csv_result)):
                if compute_iou((ymin_pts, xmin_pts, ymax_pts, xmax_pts), (
                csv_result[i][69][1], csv_result[i][69][0], csv_result[i][69][3], csv_result[i][69][2])) > iou_max\
                        and compute_iou((ymin_pts, xmin_pts, ymax_pts, xmax_pts), (
                csv_result[i][69][1], csv_result[i][69][0], csv_result[i][69][3], csv_result[i][69][2])) >= 0.5:
                    face_pose = csv_result[i][68][0:]
            if not face_pose:
                continue

            # read the face detected result with txt format and get the face bbox, which is in Square form
            fp_txt = open(os.path.join(txt_path, filename + '.txt'), 'r')
            lines = fp_txt.readlines()
            iou_max = 0
            for line in lines:
                line = line[:-1]
                S = line.split(' ')
                xmin_txt = float(S[0])
                ymin_txt = float(S[1])
                xmax_txt = float(S[2])
                ymax_txt = float(S[3])
                if compute_iou((ymin_pts, xmin_pts, ymax_pts, xmax_pts),
                               (ymin_txt, xmin_txt, ymax_txt, xmax_txt)) > iou_max\
                        and compute_iou((ymin_pts, xmin_pts, ymax_pts, xmax_pts),
                               (ymin_txt, xmin_txt, ymax_txt, xmax_txt)) >= 0.7:
                    face_bbox.append([xmin_txt, ymin_txt, xmax_txt, ymax_txt])
            fp_txt.close()

            # determine if the face is detected
            if face_bbox:
                width = xmax_txt - xmin_txt
                height = ymax_txt - ymin_txt
                adjust_value = 1.0 * (height - width) / 2.0
                xmin = int(round(max(0, xmin_txt - adjust_value)))
                ymin = int(round(ymin_txt))
                xmax = int(round(min(img.shape[1], xmin+height)))
                ymax = int(round(ymax_txt))
		print(xmax-xmin, ymax-ymin)
            else:
                width = xmax_pts - xmin_pts
                height = ymax_pts - ymin_pts
                long_size = max(width, height)
                adjust_value_x = 1.0 * (long_size - width) / 2.0
                adjust_value_y = 1.0 * (long_size - height) / 2.0
                xmin = int(round(max(0, xmin_pts - adjust_value_x)))
                xmax = int(round(min(img.shape[1], xmin+long_size)))
                ymin = int(round(max(0, ymin_pts - adjust_value_y)))
                ymax = int(round(min(img.shape[0], ymin+long_size)))
		print(xmax-xmin,ymax-ymin)
            # crop face from original img
            img_face = img[ymin:ymax, xmin:xmax, :]
            cv2.imwrite(os.path.join(face_path, filename + '.jpg'), img_face)
            # write the path of cropped face to txt
            fp_label.write(os.path.join(face_path, filename + '.jpg') + ' ')
            #fp_label.write('/home/OpenFace/300w_face/'+filename + '.jpg'+' ')
            # write the point coordinates to txt
            for i in range(68):
                fp_label.write(str(pts_result[i][0] - xmin) + ' ' + str(pts_result[i][1] - ymin) + ' ')
            # write the face pose to txt
	    '''
            rotation_vector = []
            rotation_vector.append([float(face_pose[0])])
            rotation_vector.append([float(face_pose[1])])
            rotation_vector.append([float(face_pose[2])])
            R = vec2matrix(rotation_vector)
            headpose = rotationMatrixToAngles(R)
	    '''
	    headpose = []
	    headpose.append(float(face_pose[0])/math.pi*180)
	    headpose.append(float(face_pose[1])/math.pi*180)
	    headpose.append(float(face_pose[2])/math.pi*180)
            fp_label.write(str(headpose[0]) + ' ' + str(headpose[1]) +' ' + str(headpose[2]) + '\n')
    fp_label.close()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--img_path", type=str, default='img_path',
                        help="the path to the 300w dataset")
    parser.add_argument("--csv_path", type=str, default='csv_path',
                        help="the path to the result of OpenFace")
    parser.add_argument("--txt_path", type=str, default='txt_path',
                        help="the path to the face detect")
    parser.add_argument("--face_path", type=str, default='face_path_test',
                        help="the path to the crop face saved")
    parser.add_argument("--label_path", type=str, default='label_path_test',
                        help="the path to the label of 300w dataset")
    opt = parser.parse_args()
    Generate_labels(opt.img_path, opt.csv_path, opt.txt_path, opt.face_path, opt.label_path)

4.参考github:https://github.com/lsy17096535/face-landmark/blob/master/train/mainloop.py

生成hdf5时对数据集做的预处理操作如下所示:

image   人脸

1. resize(img,(60,60)).astype('f4')
2. cvtcolor(BGR2GRAY).reshape(1,60,60)
3. m,s = cv2.meanstdDev(image)
4. image = (image - m) / (1.e-6 + s)
landmarks  关键点
1. 乘以scale系数  rx=60/face img width  ry=60/face img height
2. 归一化  /60
poses  头部姿态
1. 归一化  /50

执行代码:Generate_hdf5.py

import sys,os
import cv2
import numpy as np
from shutil import copyfile
caffe_root = 'caffe/python/'
sys.path.insert(0, caffe_root + 'python')
import caffe
import h5py
import argparse

IMAGE_SIZE = 60  #fixed size to all images

def Generate_hdf5(train_txt_file, train_h5_file, train_h5_list_file):
    """
	   Generate hdf5 format based on the txt label
	   :param train_txt_file: label in txt format (imgpath x1 y1 x2 y2 ...... x68 y68 pitch yaw roll)
	   :param train_h5_file: h5 file to be saved
	   :param train_h5_list_file: list file in txt format to be saved
	   :return:
	"""
    with open(train_txt_file,'r') as T:
        lines = T.readlines()
        HD5Images = np.zeros([len(lines), 1, IMAGE_SIZE, IMAGE_SIZE], dtype='float32')
        HD5Landmarks = np.zeros([len(lines), 136], dtype='float32')
        HD5Poses = np.zeros([len(lines), 3], dtype='float32')

        for i,l in enumerate(lines):
            sp = l.split(' ')
            print(sp[0])
            img = cv2.imread(sp[0])
            height,width = img.shape[0], img.shape[1]
            rx,ry = 1.0*IMAGE_SIZE/width, 1.0*IMAGE_SIZE/height
            res = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE), 0.0, 0.0, interpolation=cv2.INTER_CUBIC)
            image = res.astype('f4')
            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).reshape(1, IMAGE_SIZE, IMAGE_SIZE)
            m, s = cv2.meanStdDev(image)
            image = (image - m) / (1.e-6 + s)
            HD5Images[i, :] = image
            label_ = np.zeros([136], dtype='float32')
            pose_ = np.zeros([3], dtype='float32')

            for j in range(136):
                if (j + 1) % 2:
                    scale_factor = rx
                else:
                    scale_factor = ry
                label_[j] = float(sp[j + 1]) * float(scale_factor)
                label_[j] = label_[j]/(1.0*IMAGE_SIZE)
                label_[j] = label_[j].astype('f4')
            HD5Landmarks[i, :] = label_[:]
            # print(HD5Landmarks[i, :])

            for j in range(3):
                normalize_factor = 50
                pose_[j] = float(sp[j + 1 + 136]) / float(normalize_factor)
                pose_[j] = pose_[j].astype('f4')
       		HD5Poses[i, :] = pose_[:]

		with h5py.File(train_h5_file, 'w') as H:
			H.create_dataset('data', data=HD5Images)
			H.create_dataset('label', data=HD5Landmarks)
			H.create_dataset('pose', data=HD5Poses)
		with open(train_h5_list_file, 'w') as L:
			L.write(train_h5_file)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--train_txt_file",type=str,default="label_path/300w_label.txt", help="path to label in txt format")
    parser.add_argument("--train_h5_file",type=str,default="test.h5",help="path to generated h5 file")
    parser.add_argument("--train_h5_list_file",type=str,default="test_h5_list.txt",help="path to generated he list file")
    opt=parser.parse_args()
    Generate_hdf5(opt.train_txt_file, opt.train_h5_file, opt.train_h5_list_file)

5. 可视化hdf5文件是否正确

执行代码:visual_h5.py

import sys,os
import cv2
import h5py
import numpy as np

f = h5py.File('../data/300w_ori/train.h5','r')
data = f['data']
label = f['label']
pose = f['pose']
num = data.shape[0]
for i in range(1,100):
    # print(data.shape)
    img = np.array(data[i,0,:,:]*255)
    image = np.zeros([img.shape[0],img.shape[1],3],dtype='float32')
    image[:,:,0] = img[:]
    image[:,:,1] = img[:]
    image[:,:,2] = img[:]
    print(image.shape)
    landmark = np.array(label[i,:])
    print(landmark)
    # print(landmark.shape)
    cv2.imwrite(os.path.join('../data/300w_ori/result',str(i)+'.jpg'),image)
    img = cv2.imread(os.path.join('../data/300w_ori/result',str(i)+'.jpg'))
    for j in range(68):
        x = int(round(landmark[2*j]*60))
        y = int(round(landmark[2*j+1]*60))
        # print(x,y)
        cv2.circle(img,(x,y),1,(0,0,255),1)
    Euler = np.array(pose[i,:])
    # print(Euler.shape)
    cv2.imwrite(os.path.join('../data/300w_ori/result',str(i)+'.jpg'), img)
   

发布了38 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u014090429/article/details/102857998
今日推荐