PaddlePaddle实现人脸识别系统一——人脸数据集的获取

  |   0 评论   |   0 浏览   |   夜雨飘零

前言

开发人脸识别系统,人脸数据集是必须的。所以在我们开发这套人脸识别系统的准备工作就是获取人脸数据集。本章将从公开的数据集到自制人脸数据集介绍,为我们之后开发人脸识别系统做好准备。

公开人脸数据集

公开的人脸数据集有很多,本中我们就介绍几个比较常用的人脸数据集。

CelebA 人脸数据集

官方提供的下载地址:https://pan.baidu.com/s/1eSNpdRG#list/path=%2F

该数据集下载后有 3 个文件夹,Anno 文件夹是存放标注文件的,Eval 文件夹是存放评估列表文件的,Img 文件是存放图片文件的。
Img 中有 3 中类型的图像文件,其中

  • img_align_celeba.zip 是经过对人脸居中,裁剪,并统一大小为 178*178 的 jpg 图片;
  • img_align_celeba_png.7z 中的图片跟 img_align_celeba.zip 中的图片一样,唯一不同的是这些图片是 png 格式的,所以这些图片要大得多。
  • img_celeba.7z 这个是人脸图片的原始图片,没有经过居中裁剪等处理的图片。

Anno 文件夹中有 5 个标注文件,其中

  1. identity_CelebA.txt 是指定每张图片对应的人脸标签,格式为 图片名称 人脸ID
000001.jpg 2880
000002.jpg 2937
000003.jpg 8692
000004.jpg 5805
  1. list_attr_celeba.txt 文件是标注人脸属性的,比如该人脸是否黑色头发,是否戴眼镜等等
5_o_Clock_Shadow Arched_Eyebrows Attractive Bags_Under_Eyes Bald Bangs Big_Lips Big_Nose Black_Hair Blond_Hair Blurry Brown_Hair Bushy_Eyebrows Chubby Double_Chin Eyeglasses Goatee Gray_Hair Heavy_Makeup High_Cheekbones Male Mouth_Slightly_Open Mustache Narrow_Eyes No_Beard Oval_Face Pale_Skin Pointy_Nose Receding_Hairline Rosy_Cheeks Sideburns Smiling Straight_Hair Wavy_Hair Wearing_Earrings Wearing_Hat Wearing_Lipstick Wearing_Necklace Wearing_Necktie Young 
000001.jpg -1  1  1 -1 -1 -1 -1 -1 -1 -1 -1  1 -1 -1 -1 -1 -1 -1  1  1 -1  1 -1 -1  1 -1 -1  1 -1 -1 -1  1  1 -1  1 -1  1 -1 -1  1
000002.jpg -1 -1 -1  1 -1 -1 -1  1 -1 -1 -1  1 -1 -1 -1 -1 -1 -1 -1  1 -1  1 -1 -1  1 -1 -1 -1 -1 -1 -1  1 -1 -1 -1 -1 -1 -1 -1  1
000003.jpg -1 -1 -1 -1 -1 -1  1 -1 -1 -1  1 -1 -1 -1 -1 -1 -1 -1 -1 -1  1 -1 -1  1  1 -1 -1  1 -1 -1 -1 -1 -1  1 -1 -1 -1 -1 -1  1
  1. list_bbox_celeba.txt 文件是标注人脸在图片中的位置,标注信息为 image_id x_1 y_1 width height
image_id x_1 y_1 width height
000001.jpg    95  71 226 313
000002.jpg    72  94 221 306
000003.jpg   216  59  91 126
000004.jpg   622 257 564 781
  1. list_landmarks_align_celeba.txt 该文件是居中后图片的人脸关键点的标注文件,一共有 5 个关键点,为 眼睛、鼻子和嘴角
lefteye_x lefteye_y righteye_x righteye_y nose_x nose_y leftmouth_x leftmouth_y rightmouth_x rightmouth_y
000001.jpg 69  109  106  113   77  142   73  152  108  154
000002.jpg 69  110  107  112   81  135   70  151  108  153
000003.jpg 76  112  104  106  108  128   74  156   98  158
000004.jpg 72  113  108  108  101  138   71  155  101  151
  1. list_landmarks_celeba.txt 文件是原图片中人脸关键点的位置。
lefteye_x lefteye_y righteye_x righteye_y nose_x nose_y leftmouth_x leftmouth_y rightmouth_x rightmouth_y
000001.jpg 165  184  244  176  196  249  194  271  266  260
000002.jpg 140  204  220  204  168  254  146  289  226  289
000003.jpg 244  104  264  105  263  121  235  134  251  140

LFW 数据集

数据集下载地址:http://mmlab.ie.cuhk.edu.hk/archive/CNN/data/train.zip

LFW 数据集解压之后得到 2 个文件夹和 2 个文本文件。

  • lfw_5590net_7876 文件夹都是存放人脸图片的
  • testImageList.txttrainImageList.txt 都是标注信息文本文件,标注信息为 图片文件、人脸box的坐标位置、人脸5个关键点的坐标位置
lfw_5590\Aaron_Eckhart_0001.jpg 84 161 92 169 106.250000 107.750000 146.750000 112.250000 125.250000 142.750000 105.250000 157.750000 139.750000 161.750000
lfw_5590\Aaron_Guiel_0001.jpg 85 172 93 181 100.250000 111.250000 145.750000 116.750000 124.250000 136.750000 92.750000 159.750000 138.750000 163.750000
lfw_5590\Aaron_Peirsol_0001.jpg 88 173 94 179 106.750000 113.250000 146.750000 113.250000 129.250000 139.750000 108.250000 153.250000 146.750000 152.750000
lfw_5590\Aaron_Pena_0001.jpg 67 176 83 192 101.750000 116.750000 145.250000 103.750000 125.250000 136.750000 119.750000 163.750000 146.250000 155.750000

WIDER 人脸数据集

官方提供图片下载地址:http://pan.baidu.com/s/1c0DfSmW
标注文件下载地址:http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/support/bbx_annotation/wider_face_split.zip

WIDER_train.zip 解压是得到人脸图片,每张图片可呢个包含多个人脸。

wider_face_train_bbx_gt.txt 文件也同样是标注人脸所在图片的位置,不过这里标注的方式,标注的信息为 xmin ymin width height。第一行是图片的路径,第二行是标注的数量,因为图片中可能有多张人脸,第三就是图片的标注信息。

0--Parade/0_Parade_marchingband_1_849.jpg
1
449 330 122 149 0 0 0 0 0 0 
0--Parade/0_Parade_Parade_0_904.jpg
1
361 98 263 339 0 0 0 0 0 0 

通过上面的标注文件可以生成 wider_face_train.txt,标注的方式变成 xmin ymin xmax ymax。有些图片有多个标注数据,因为这个数据集的图片中多人脸的,跟前面的数据集不同,前面的都是一张图片只有一张人脸。

1--Handshaking/1_Handshaking_Handshaking_1_288 336.00 82.00 448.00 244.00 
1--Handshaking/1_Handshaking_Handshaking_1_924 271.42 425.64 508.92 681.64 
1--Handshaking/1_Handshaking_Handshaking_1_866 364.42 894.49 451.76 993.88 545.13 771.01 623.44 879.44 186.73 825.22 256.00 945.69 
1--Handshaking/1_Handshaking_Handshaking_1_164 223.46 100.68 351.16 275.03 532.87 100.68 665.48 279.94 
1--Handshaking/1_Handshaking_Handshaking_1_243 393.00 198.00 426.00 245.00 

制作人脸数据集

下面我们就介绍如何制作自己的人脸数据集,项目的开源地址:https://github.com/yeyupiaoling/FaceDataset 。该项目可以分为两个阶段,第一阶段是人脸图片的获取和简单的清洗,第二阶段是人脸图片的高级清洗和标注人脸信息。人脸信息的标注和清洗使用到了百度的人脸识别服务。

第一阶段

爬取人脸图片的核心思路就是获取中国明星的名字,然后使用明星的名字作为图片搜索的关键字进行获取图片,然后删除下载过程损坏的图片和没有包含人脸的图片,或者过多人脸的图片(我们只保存一张图片只包含一张人脸的图片)。

首先获取中国明星的名字,该功能主要在 get_star_name.py 中实现。获取明显的名字核心代码如下,获取的名字不能保证百分之百正确,所以可能需要手动去检查。

# 获取明星的名字并保存到文件中
def get_page(pages, star_name):
    params = []
    # 设置访问的请求头,包括分页数和明星所在的地区
    for i in range(0, 12 * pages + 12, 12):
        params.append({
            'resource_id': 28266,
            'from_mid': 1,
            'format': 'json',
            'ie': 'utf-8',
            'oe': 'utf-8',
            'query': '明星',
            'sort_key': '',
            'sort_type': 1,
            'stat0': '',
            'stat1': star_name,
            'stat2': '',
            'stat3': '',
            'pn': i,
            'rn': 12})

    # 请求的百度接口获取明星的名字
    url = 'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php'

    x = 0
    # 根据请求头下载明星的名字
    for param in params:
        try:
            # 获取请求数据
            res = requests.get(url, params=param, timeout=50)
            # 把网页数据转换成json数据
            js = json.loads(res.text)
            # 获取json中的明星数据
            results = js.get('data')[0].get('result')
        except AttributeError as e:
            print('【错误】出现错误:%s' % e)
            continue

        # 从数据中提取明星的名字
        for result in results:
            img_name = result['ename']
            f.write(img_name + '\n',)

然后根据明星的名字从网上下载图片,该功能主要在 download_image.py 中实现,以下就是下载图片的核心代码片段。

# 获取百度图片下载图片
def download_image(key_word, download_max):
    download_sum = 0
    str_gsm = '80'
    # 把每个明显的图片存放在单独一个文件夹中
    save_path = 'star_image' + '/' + key_word
    if not os.path.exists(save_path):
        os.makedirs(save_path)
    while download_sum < download_max:
        # 下载次数超过指定值就停止下载
        if download_sum >= download_max:
            break
        str_pn = str(download_sum)
        # 定义百度图片的路径
        url = 'http://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&' \
              'word=' + key_word + '&pn=' + str_pn + '&gsm=' + str_gsm + '&ct=&ic=0&lm=-1&width=0&height=0'
        try:
            # 获取当前页面的源码
            result = requests.get(url, timeout=30).text
            # 获取当前页面的图片URL
            img_urls = re.findall('"objURL":"(.*?)",', result, re.S)
            if len(img_urls) < 1:
                break
            # 把这些图片URL一个个下载
            for img_url in img_urls:
                # 获取图片内容
                img = requests.get(img_url, timeout=30)
                img_name = save_path + '/' + str(uuid.uuid1()) + '.jpg'
                # 保存图片
                with open(img_name, 'wb') as f:
                    f.write(img.content)
                with open('image_url_list.txt', 'a+', encoding='utf-8') as f:
                    f.write(img_name + '\t' + img_url + '\n')
                download_sum += 1
                if download_sum >= download_max:
                    break
        except Exception as e:
            download_sum += 1
            continue

下载图片完成之后,有很多损坏的图片,需要把这些损坏的图片删除,该功能主要在 delete_error_image.py 实现。下面是删除损坏图片的核心代码片段。

# 删除不是JPEG或者PNG格式的图片
def delete_error_image(father_path):
    # 获取父级目录的所有文件以及文件夹
    image_paths = []
    for root, dirs, files in os.walk(father_path):
        for file in files:
            image_paths.append(os.path.join(root, file))
    for image in tqdm(image_paths):
        try:
            # 获取图片的类型
            image_type = imghdr.what(image)
            # 如果图片格式不是JPEG同时也不是PNG就删除图片
            if image_type is not 'jpeg' and image_type is not 'png':
                os.remove(image)
                continue
            # 删除灰度图
            img = numpy.array(Image.open(image))
            if len(img.shape) is 2:
                os.remove(image)
        except:
            os.remove(image)

下载的图片中可能没有人脸,或者包含多张人脸,所以我们要把这些图片删除掉,该功能主要在 delete_more_than_one.py 中实现。删除没有人脸或者过多人脸图片的关键代码片段如下。

# 删除两个人脸以上的图片或者没有人脸的图片
def delete_image(result, image_path):
    try:
        face_num = int(result['result']['face_num'])
        if face_num is not 1:
            os.remove(image_path)
        else:
            face_type = result['result']['face_list'][0]['face_type']['type']
            probability = result['result']['face_list'][0]['face_type']['probability']
            if face_type == 'cartoon' and probability > 0.8:
                os.remove(image_path)
    except:
        os.remove(image_path)

第二阶段

第二阶段属于高级清理和标注人脸信息。这一个阶段首先是把每个文件夹中包含相同一个人的图片较多的人脸,选择其中一个作为主人脸图片。然后使用这个主图片来对比其他图片,判断是否是同一个人,如果不是就删除该图片。接着就删除 URL 文件中,一些删除的文件对应的 URL。最好就使用百度的人脸检测服务标注清理后的图片,最终得到一个人脸数据集。

首先是从众多图片中选择一个主图片,这个功能主要在 find_same_person.py 中实现,以下是获取主图片的核心代码片段。这个程序消耗时间比较多,其实也可以通过手动标记的方式,选择一个主的人脸图片,当然这个是非常大的一个工作量。

# 寻找同一个人的作为0.jpg,作为主的参考图片
def find_same_person(person_image_path):
    # 获取该人中的所有图片
    image_paths = os.listdir(person_image_path)
    if '0.jpg' in image_paths:
        image_paths.remove('0.jpg')
    # 临时选择第一个作为主图片
    temp_image = os.path.join(person_image_path, image_paths[0])
    main_path = os.path.join(person_image_path, '0.jpg')
    if os.path.exists(main_path):
        os.remove(main_path)
    shutil.copyfile(temp_image, main_path)
    for main_image in image_paths:
        # 获取主图片的全路径
        main_image = os.path.join(person_image_path, main_image)
        # 获取主图片的base64
        main_img = get_file_content(main_image)
        # 统计相同人脸数量
        same_sum = 0
        for other_image in image_paths:
            # 获取其他对比人脸的全路径
            other_image = os.path.join(person_image_path, other_image)
            # 获取其他对比图片的base64
            other_img = get_file_content(other_image)
            # 获取对比结果
            result = match_image(main_img, other_img)
            time.sleep(0.5)
            # 判断是不是同一个人
            if if_same_person(result):
                same_sum += 1
            # 当相同的人脸超过6个是就做为主图片
            if same_sum >= 6:
                if os.path.exists(main_path):
                    os.remove(main_path)
                shutil.copyfile(main_image, main_path)
                break
        if same_sum > 6:
            break

然后删除与主图片不是同一个人的图片,这个功能主要在 delete_not_same_person.py 中实现,以下是删除不是同一个人脸的图片核心代码片段。

        for name_path in tqdm(name_paths):
            image_paths = os.listdir(os.path.join(father_path, name_path))
            for image_path in image_paths:
                # 正确图片的路径
                main_image = os.path.join(father_path, name_path, '0.jpg')
                # 要对比的图片
                img_path = os.path.join(father_path, name_path, image_path)
                # 获取图片的base64
                main_img = get_file_content(main_image)
                img = get_file_content(img_path)
                time.sleep(0.5)
                # 预测图片并进行处理
                result = match_image(main_img, img)
                delete_image(result, img_path)
            shutil.move(src=os.path.join(father_path, name_path), dst=os.path.join('star_image', name_path))

然后执行 delete_surplus_url.py 程序,从 image_url_list.txt 中删除本地不存在图片对应的 URL。

    # 删除图片过少的文件夹
    delete_too_few()
    list_path = 'image_url_list.txt'
    lines = get_txt_list(list_path)
    # 重新改写这个文件
    with open(list_path, 'w', encoding='utf-8') as f:
        for line in lines:
            exist = file_if_exist(line)
            # 把存在的文件的list保留
            if exist:
                f.write(line)

最后执行 annotate_image.py 程序,利用百度人脸检测接口标注人脸图片,以下是标注人脸的核心代码片段。

# 把预测结果和图片的URL写入到标注文件中
def annotate_image(result, image_path, image_url):
    # 获取文件夹名字,并得到已经记录多少人
    father_path = os.path.dirname(image_path)
    image_name = os.path.basename(image_path).split('.')[0]
    # 获取明星的名字
    name = father_path.split('/')[-1]
    # 把这些名字转换成数字标号
    names.add(name)
    num_name = str(len(names) - 1)
    annotation_path = os.path.join('annotations', num_name)
    dict_names_list.append((name, num_name))
    annotation_file_path = os.path.join(annotation_path, str(image_name) + '.json')
    # 创建存放标注文件的文件夹
    if not os.path.exists(annotation_path):
        os.makedirs(annotation_path)

    try:
        # 名字
        name = name
        # 年龄
        age = result['result']['face_list'][0]['age']
        # 性别,male:男性 female:女性
        gender = result['result']['face_list'][0]['gender']['type']
        # 脸型,square: 正方形 triangle:三角形 oval: 椭圆 heart: 心形 round: 圆形
        face_shape = result['result']['face_list'][0]['face_shape']['type']
        # 是否带眼镜,none:无眼镜,common:普通眼镜,sun:墨镜
        glasses = result['result']['face_list'][0]['glasses']['type']
        # 表情,none:不笑;smile:微笑;laugh:大笑
        expression = result['result']['face_list'][0]['expression']['type']
        # 颜值,范围0-100
        beauty = result['result']['face_list'][0]['beauty']
        # 人脸在图片中的位置
        location = str(result['result']['face_list'][0]['location']).replace("'", '"')
        # 人脸旋转角度参数
        angle = str(result['result']['face_list'][0]['angle']).replace("'", '"')
        # 72个特征点位置
        landmark72 = str(result['result']['face_list'][0]['landmark72']).replace("'", '"')
        # 4个关键点位置,左眼中心、右眼中心、鼻尖、嘴中心
        landmark = str(result['result']['face_list'][0]['landmark']).replace("'", '"')
        # 拼接成符合json格式的字符串
        txt = '{"name":"%s", "image_url":"%s","age":%f, "gender":"%s", "glasses":"%s", "expression":"%s", "beauty":%f, "face_shape":"%s", "location":%s, "angle":%s, "landmark72":%s, "landmark":%s}' \
              % (name, image_url, age, gender, glasses, expression, beauty, face_shape, location, angle, landmark72,
                 landmark)
        # 转换成json数据并格式化
        json_dicts = json.loads(txt)
        json_format = json.dumps(json_dicts, sort_keys=True, indent=4, separators=(',', ':'))
        # 写入标注文件
        with open(annotation_file_path, 'w', encoding='utf-8') as f_a:
            f_a.write(json_format)
    except Exception as e:
        os.remove(image_path)
        pass

整个项目完成的时间的非常久的,特别是使用到百度 AI 服务的程序,为了不出现每秒访问次数超过 2 次(免费的版本是每秒自动访问 2 次),所在做了休眠处理,所以这样浪费了不少时间。

项目 GitHub 地址: https://github.com/yeyupiaoling/FaceDataset

参考资料

  1. http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
  2. http://mmlab.ie.cuhk.edu.hk/archive/CNN_FacePoint.htm
  3. http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/
  4. http://ai.baidu.com/docs#/Face-Detect-V3/top

标题:PaddlePaddle实现人脸识别系统一——人脸数据集的获取
作者:夜雨飘零
地址:https://blog.doiduoyi.com/articles/1584973882516.html

评论

发表评论