前言

在调研人脸关键点检测算法时,找到了一篇较新的人脸106个点检测的论文Grand Challenge of 106-Point Facial Landmark Localization, 进一步深挖该篇论文,发现新构建的人脸关键点数据集----JD- landmark,并且已经开源了,于是乎,立马给项目负责人Dr. Hailin Shi发了封希望能够获得一份该数据集的邮件,数据集就这么快速的到手了,感谢为该项目付出的所有人。

JD-landmark简介

下载的数据集是JD-landmark.zip,执行以下操作:

unzip JD-landmark.zip   //需要输入解压密码,详细请查看回复你的邮件
cd JD-landmark

解压后的数据分为3部分 Train, Val and Test,每个目录下又分landmark、picture、picture_crop、rect;文件目录层级结构如下图所示:

JD-landmark
|   |--Test
|   |    |-landmark
|   |    |     |-000001.jpg.txt
|   |    |-picture
|   |    |   |-000001.jpg
|   |    |-picture_crop
|   |    |    |-000001.jpg
|   |    |-rect
|   |        |-000001.jpg.rect
|   |-Train
|   |    |-landmark
|   |    |     |-000001.jpg.txt
|   |    |-picture
|   |    |   |-000001.jpg
|   |    |-picture_crop
|   |    |    |-000001.jpg
|   |    |-rect
|   |        |-000001.jpg.rect
|   |---Val
|   |    |-landmark
|   |    |     |-000001.jpg.txt
|   |    |-picture
|   |    |   |-000001.jpg
|   |    |-picture_crop
|   |    |    |-000001.jpg
|   |    |-rect
|   |        |-000001.jpg.rect

其中:
landmark:106个关键点文件,文件命名为xxxx.jpg.txt,[x0 y0 x1 y1 … x105 y105]
picture: 原始图片,文件命名为xxxx.jpg
picture_crop:根据人脸框截取出来的人脸图片
rect: 人脸位置矩形框,[tx,ty,bx,by]
可以简单看下效果:

import os, sys
import cv2
import glob
import matplotlib.pyplot as pltJdLandmarkPath='/path/JD-landmark'
test_path=os.path.join(JdLandmarkPath, 'Test')
test_landmark_path=os.path.join(test_path, 'landmark')
test_rect_path=os.path.join(test_path, 'rect')
test_imgs_path=os.path.join(test_path, 'picture')test_img=os.path.join(test_imgs_path, '000001.jpg')
test_landmark=os.path.join(test_landmark_path, '000001.jpg.txt')
test_rect=os.path.join(test_rect_path, '000001.jpg.rect')
# imread image
img=cv2.imread(test_img)
# load landmark
with open(test_landmark, 'r') as f:lines=f.readlines()keypoits=lines[1:] # line 1 is keypoint num, skip itfor poit in keypoits:x, y = poit.split(' ')cv2.circle(img, (int(float(x)), int(float(y))), 4, (0, 0, 255), -1)
# load rect
with open (test_rect, 'r') as f:lines = f.readlines()x, y, bx, by = lines[0].split(' ')cv2.rectangle(img, (int(x), int(y)), (int(bx), int(by)), (255,0,0), 2)cv2.imwrite('img.jpg', img)
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img)

结果如下图所示:

Test/picture/000001.jpg

其他数据集亦是如此,可以自行查看。

JD-landmark 数据预处理

这里选择的处理方式是将图片路径,landmark关键点和人脸位置矩形框写到一个文件中去,文件格式为 文件名 106个人脸关键点 人脸位置矩形框,预处理代码如下所示:

import os, sys
import cv2def gen_JDlandmark_label_txt(fpath):#### train datasetstrain_path=os.path.join(JdLandmarkPath, 'Train')train_label_txt=os.path.join(JdLandmarkPath, 'jd_106pt_train_label.txt')train_landmark_path=os.path.join(train_path, 'landmark')train_rect_path=os.path.join(train_path, 'rect')train_imgs_path=os.path.join(train_path, 'picture')with open(train_label_txt, 'w') as f:train_imgs=os.listdir(train_imgs_path)train_imgs.sort()for img in train_imgs:picture_path = os.path.join(train_imgs_path, img)landmark_path=picture_path.replace('picture', 'landmark')+'.txt'landmark=''with open(landmark_path, 'r') as landmarkf:lines=landmarkf.readlines()keypoints=lines[1:]for point in keypoints:x, y = point.rstrip('\n').split(' ')landmark += x + ' ' + y + ' 'rect_path=picture_path.replace('picture', 'rect')+'.rect'rect = ''with open(rect_path, 'r') as rectf:lines = rectf.readlines()tx, ty, bx, by = lines[0].split(' ')rect += tx + ' ' + ty + ' ' + bx + ' ' + bypicture_path='/'.join(picture_path.split('/')[-3:])f.write(picture_path + ' ' + landmark  + rect + '\n')#### valid datasetsvalid_path=os.path.join(JdLandmarkPath, 'Val')valid_label_txt=os.path.join(JdLandmarkPath, 'jd_106pt_val_label.txt')valid_landmark_path=os.path.join(valid_path, 'landmark')valid_rect_path=os.path.join(valid_path, 'rect')valid_imgs_path=os.path.join(valid_path, 'picture')with open(valid_label_txt, 'w') as f:valid_imgs=os.listdir(valid_imgs_path)valid_imgs.sort()for img in valid_imgs:picture_path = os.path.join(valid_imgs_path, img)landmark_path=picture_path.replace('picture', 'landmark')+'.txt'landmark=''with open(landmark_path, 'r') as landmarkf:lines=landmarkf.readlines()keypoints=lines[1:]for point in keypoints:x, y = point.rstrip('\n').split(' ')landmark += x + ' ' + y + ' 'rect_path=picture_path.replace('picture', 'rect')+'.rect'rect = ''with open(rect_path, 'r') as rectf:lines = rectf.readlines()tx, ty, bx, by = lines[0].split(' ')rect += tx + ' ' + ty + ' ' + bx + ' ' + bypicture_path='/'.join(picture_path.split('/')[-3:])f.write(picture_path + ' ' + landmark  + rect + '\n')#### test datasetstest_path=os.path.join(JdLandmarkPath, 'Test')test_label_txt=os.path.join(JdLandmarkPath, 'jd_106pt_test_label.txt')test_landmark_path=os.path.join(test_path, 'landmark')test_rect_path=os.path.join(test_path, 'rect')test_imgs_path=os.path.join(test_path, 'picture')with open(test_label_txt, 'w') as f:test_imgs=os.listdir(test_imgs_path)test_imgs.sort()for img in test_imgs:picture_path = os.path.join(test_imgs_path, img)landmark_path=picture_path.replace('picture', 'landmark')+'.txt'landmark=''with open(landmark_path, 'r') as landmarkf:lines=landmarkf.readlines()keypoints=lines[1:]for point in keypoints:x, y = point.rstrip('\n').split(' ')landmark += x + ' ' + y + ' 'rect_path=picture_path.replace('picture', 'rect')+'.rect'rect = ''with open(rect_path, 'r') as rectf:lines = rectf.readlines()tx, ty, bx, by = lines[0].split(' ')rect += tx + ' ' + ty + ' ' + bx + ' ' + bypicture_path='/'.join(picture_path.split('/')[-3:])f.write(picture_path + ' ' + landmark  + rect + '\n')print(f'gen dataset label done\n\ttrain lable_path:{train_label_txt}\n\ttest label path:{test_label_txt}\n\tvalid label_path:{valid_label_txt}')if __name__ == "__main__":JdLandmarkPath='/path/JD-landmark'gen_JDlandmark_label_txt(JdLandmarkPath)

代码运行后将在/path/JD-landmark 下生成jd_106pt_train_label.txt、jd_106pt_test_label.txt、jd_106pt_val_label.txt。内容如下图所示:

jd_106pt_test_label.txt

JD-landmark 数据增强处理

这里选择使用pytorch来做相关项目,因此数据增强也是基于pytorch的Dataset,DataLoader那套机制来处理的。

# -*- coding: utf-8 -*-
from __future__ import print_function, division
import random
import os
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
import cv2
from torchvision import transforms, utils
from PIL import Imagedef show_landmarks(image, landmarks, rects):image = image.numpy()image = np.transpose(image,(1,2,0))image = 255*(image*0.5+0.5)image = image.astype(np.uint8)image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)landmarks= landmarks.numpy()landmarks=landmarks.reshape((-1,2))rects= rects.numpy()rects=rects.reshape((-1,2))h,w = image.shape[0:2]for i in landmarks:cv2.circle(image, (int(w*i[0]), int(h*i[1]) ), 3, (255,0,0), -1 )cv2.rectangle(image, (int(rects[0][0]*w), int(rects[0][1]*h)),(int(rects[1][0]*w), int(rects[1][1]*h)), (0,255, 0), 1)return imageclass Resize(object):"""Rescale the image in a sample to a given size.Args:output_size (tuple or int): Desired output size. If tuple, output ismatched to output_size. If int, smaller of image edges is matchedto output_size keeping aspect ratio the same."""def __init__(self, output_size):assert isinstance(output_size, (int, tuple))self.output_size = output_sizedef __call__(self, sample):image, landmarks, rects = sample['image'], sample['landmarks'], sample['rects']h, w = image.shape[:2]if isinstance(self.output_size, int):if h > w:new_h, new_w = self.output_size * h / w, self.output_sizeelse:new_h, new_w = self.output_size, self.output_size * w / helse:new_h, new_w = self.output_sizenew_h, new_w = int(new_h), int(new_w)img = cv2.resize(image, (new_h, new_w))landmarks = landmarks * [new_w / w, new_h / h]rects = rects * [new_w / w, new_h / h]return {'image': img, 'landmarks': landmarks, 'rects': rects}class RandomCrop(object):"""Crop randomly the image in a sample.Args:output_size (tuple or int): Desired output size. If int, square cropis made."""def __init__(self, output_size):assert isinstance(output_size, (int, tuple))if isinstance(output_size, int):self.output_size = (output_size, output_size)else:assert len(output_size) == 2self.output_size = output_sizedef __call__(self, sample):image, landmarks, rects = sample['image'], sample['landmarks'], sample['rects']h, w = image.shape[:2]new_h, new_w = self.output_sizetop = np.random.randint(0, h - new_h)left = np.random.randint(0, w - new_w)image = image[top: top + new_h,left: left + new_w]landmarks = landmarks - [left, top]rects = rects - [left, top]return {'image': image, 'landmarks': landmarks, 'rects': rects}class ToTensor(object):"""Convert ndarrays in sample to Tensors."""def __init__(self,image_size):self.image_size = image_sizedef __call__(self, sample):image, landmarks, rects = sample['image'], sample['landmarks'], sample['rects']# swap color axis because# numpy image: H x W x C# torch image: C X H X Wimage = image.transpose((2, 0, 1))landmarks =landmarks.reshape(-1,1)landmarks =np.squeeze(landmarks)rects = np.squeeze(rects)return {'image': torch.from_numpy(image).float().div(255),'landmarks': torch.from_numpy(landmarks).float().div(self.image_size),'rects': torch.from_numpy(rects).float().div(self.image_size)}class RandomFlip(object):def __call__(self, sample):image, landmarks, rects = sample['image'], sample['landmarks'], sample['rects']if random.random()<0.5:image = cv2.flip(image,1)landmarks[:,0] = image.shape[1]-landmarks[:,0]rects[:,0] = image.shape[1]-rects[:,0]return {'image': image, 'landmarks': landmarks, 'rects': rects}class RandomRotate(object):def __call__(self, sample):image, landmarks, rects = sample['image'], sample['landmarks'], sample['rects']img_h,img_w = image.shape[0:2]center = (img_w//2, img_h//2)random_degree=np.random.uniform(-15.0, 15.0)rot_mat = cv2.getRotationMatrix2D(center, random_degree, 1)img_rotated_by_alpha = cv2.warpAffine(image, rot_mat, (img_w, img_h))rotated_landmark = np.asarray([(rot_mat[0][0]*x+rot_mat[0][1]*y+rot_mat[0][2],rot_mat[1][0]*x+rot_mat[1][1]*y+rot_mat[1][2]) for (x, y) in landmarks])rotated_rects = np.asarray([(rot_mat[0][0]*x+rot_mat[0][1]*y+rot_mat[0][2],rot_mat[1][0]*x+rot_mat[1][1]*y+rot_mat[1][2]) for (x, y) in rects])return {'image': img_rotated_by_alpha, 'landmarks': rotated_landmark, 'rects': rotated_rects}class Normalize(object):def __init__(self,mean,std):self.mean = meanself.std = stddef __call__(self, sample):image = sample['image']for t, m, s in zip(image, self.mean, self.std):t.sub_(m).div_(s)sample['image'] = imagereturn sampleclass FaceLandmarksDataset(Dataset):"""Face Landmarks dataset."""def __init__(self, label_dict_list, point_num=106, transform=None):#################################################################### Initialize paths, transforms, and so on#################################################################self.images = []self.landmarks = []self.rects = []for label_dict in label_dict_list:label_frame = pd.read_csv(label_dict["label_file"], sep=" ", header=None)for infor  in label_frame.iterrows():imgpath = os.path.join(label_dict["root_dir"], infor[1][0])landmark = infor[1][1: (2*point_num+1) ].values.astype(np.int).reshape((-1,2))rect = infor[1][2*point_num+1: ].values.astype(np.int).reshape((-1,2))self.images.append(imgpath)self.landmarks.append(landmark)self.rects.append(rect)self.transform = transformdef __len__(self):#################################################################### Indicate the total size of the dataset#################################################################return len(self.images)def __getitem__(self, index):################################################################## 1. Read from file (using numpy.fromfile, PIL.Image.open)# 2. Preprocess the data (torchvision.Transform).# 3. Return the data (e.g. image and label)#################################################################image = cv2.imread(self.images[index])image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)landmarks = self.landmarks[index]rect = self.rects[index]sample = {'image': image, 'landmarks': landmarks, 'rects': rect}if self.transform:sample = self.transform(sample)return sampleif __name__=="__main__":test_transform = transforms.Compose([Resize((112,112)),RandomCrop(96),RandomFlip(),RandomRotate(),ToTensor(96),Normalize([ 0.5, 0.5, 0.5 ],[ 0.5, 0.5, 0.5 ])])testset  = FaceLandmarksDataset([{"root_dir":"/path/JD-landmark","label_file":"/path/JD-landmark/jd_106pt_test_label.txt"}],point_num=106,transform=test_transform)print('FaceLandmarksDataset testset picture numbers:', len(testset))test_loader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=True, num_workers=1)result=[] for sample in test_loader:for i in range(len(sample["image"])):image = sample["image"][i]landmark = sample["landmarks"][i]rect = sample["rects"][i]rimg = show_landmarks(image, landmark, rect)result.append(rimg)   break# show whatever you want        idx=1for i in range(0, len(result)):  try:plt.ion()plt.figure(idx)plt.imshow(result[i])except:passfinally:idx+=1

The End!!!!

FaceLandmark dataset Augment相关推荐

  1. 【医学+深度论文:F05】2018 automatic optic disk and cup segmentation of fundus images using deep learning

    05 automatic optic disk and cup segmentation of fundus images using deep learning 2018 Method :分割,视盘 ...

  2. matlab mobilenet v2,MobileNetV2-SSDLite代码分析-6 VOC Dataset

    class VOCDataset: 初始化 主要是确定了一下各个文件的目录,确定了class_names def __init__(self, root, transform=None, target ...

  3. yolov4_u5版复现—3. 数据读入 dataset.py

    1.首先需要了解一下Pytorch的DataLoader, DataSet, Sampler之间的关联 其中DataSet类来自from torch.utils.data import Dataset ...

  4. 【Tensorflow 2.0 正式版教程】tf.data.Dataset的基本使用方法

    Tensorflow 2.0中提供了专门用于数据输入的接口tf.data.Dataset,可以简洁高效的实现数据的读入.打乱(shuffle).增强(augment)等功能.下面以一个简单的实例讲解该 ...

  5. MindSpore数据集mindspore::dataset

    MindSpore数据集mindspore::dataset ResizeBilinear #include <image_process.h> bool ResizeBilinear(L ...

  6. 目标检测数据集The Object Detection Dataset

    目标检测数据集The Object Detection Dataset 在目标检测领域,没有像MNIST或Fashion MNIST这样的小数据集.为了快速测试模型,我们将组装一个小数据集.首先,我们 ...

  7. datatable和dataset的区别

    DataSet 是离线的数据源 DataTable 是数据源中的表.当然也可以自己建一张虚表.插入数据库中 DataSet是DataTable的容器DataSet可以比作一个内存中的数据库,DataT ...

  8. [pcl::VoxelGrid::applyFilter] Leaf size is too small for the input dataset. Integer indices would ov

    1. 报错日志: Python-pcl 点云下采样时报错如下: [pcl::VoxelGrid::applyFilter] Leaf size is too small for the input d ...

  9. 『TensorFlow』数据读取类_data.Dataset

    一.资料 参考原文: TensorFlow全新的数据读取方式:Dataset API入门教程 API接口简介: TensorFlow的数据集 二.背景 注意,在TensorFlow 1.3中,Data ...

最新文章

  1. 安装 Enthought Tool Suite 时遇到的问题
  2. continue和break的区别
  3. 010_SpringBoot视图层技术thymeleaf-变量输出与字符串操作
  4. Android给自定义按键添加广播和通过广播给当前焦点输入框赋值
  5. *CTF MineGame
  6. 产品运行所需的信息检索失败_为服务业注入新活力,华北工控推出服务机器人专用计算机产品方案...
  7. 多商户商城源码_多商户小程序开发搭建?开达应用多商户入驻商城小程序制作教程...
  8. python+appium自动化测试如何控制App的启动和退出
  9. c语言枪图形,6个变态的C语言Hello World程序[转载]
  10. JavaScript面向对象那些事
  11. 可公度线段与欧几里得(Euclid)算法
  12. [日期]字符串转Data对象
  13. python编写txt阅读器程序_Python 制作简易小说阅读器
  14. C++ UPD广播异步发包工具
  15. csv文件超过104万数据怎么办
  16. c语言字符数组讲课PPT,C语言数组应用与字符串.ppt
  17. 信息系统项目管理师核心考点(六十五)信息安全基础知识网络安全
  18. 墒情监测站智慧农研高标准农田设施
  19. 怎么识别手写的文字?办公常备软件说明
  20. TA 认识 unity shader最基本的代码结构与书写01

热门文章

  1. 坐标系基础相关知识:右手定则
  2. 为什么越来越多的网站域名不加www前缀?
  3. android 克隆攻击原理,通过CTF学习Android漏洞(炸弹引爆+dex修复)2015 RCTF / 攻防世界高手区 where...
  4. 用js来判断用户端浏览器(判断QQ浏览器、谷歌浏览器)方法
  5. matlab求逆运算左除(\)和右除(/),inv,pinv的用法及区别
  6. python爬虫新浪微博
  7. 关于“拜占庭将军算法”byzantine generals problem
  8. Java字符串首尾显示,中间隐藏
  9. 笔记本电脑锁屏密码能输入,但解锁后键盘不好用
  10. dagger2 android封装,Dagger2 Android应用:@Component和@Module