处理筛选CelebA人脸数据集

CalebA人脸数据集(官网链接)是香港中文大学的开放数据,包含10,177个名人身份的202,599张人脸图片,并且都做好了特征标记,这对人脸相关的训练是非常好用的数据集。

image.png

别看只是一堆人脸,他们很贴心地做好了特征标记,也就是说,你可以找到类似下面这些标签:

image.png

更贴心的是,他们除了Google盘的下载方式,还为国内研究人员提供了百度网盘的下载链接,这在他们官方都可以找到。

不过刚进入百度网盘的文件,可能有点迷,不知道什么文件是干嘛的,也不知道怎么处理,尤其是对于新手来说,这里就总结一下。

文件含义

进入百度网盘可以看到多个文件,但是都是干什么的呢?应该下载哪个来用呢?

image.png

在动手下载之前,最好先读读README文件,里面有比较详细的描述,这里只简单介绍一下:

  • Img文件夹下是所有图片,图片又分三类文件:
image.png

其中“img_celeba.7z”文件夹是纯“野生”文件,也就是从网络爬取的没有做裁剪的图片,要解压的话需要整个文件夹一起解压;“img_align_celeba_png.7z”和“img_align_celeba.zip”是把“野生”文件裁剪出人脸部分之后的图片,其中“img_align_celeba_png.7z”是png格式的,比较大,也要整个文件夹一起解压,“img_align_celeba.zip”是jpg格式的,比较小,1G多,我采用的是这个文件,直接解压即可。

image.png

不过需要注意的是里面的图片并不是正方形的,所以如果你的网络需要方形图片输入,自己还得处理一遍,后文有这部分的代码。

图中可以看到,人脸图片的名字只是简单的编号,那肤色、发色、眼镜、性别等特征标签在哪呢,在之前的“Anno”文件夹中:

image.png

第一个“list_attr_celeba.txt”文本文件就记录了每一张图片的特征标签:

image.png

如图所示,第一行表示图片个数,第二行是特征类型,以空格分开,可以看到有超多特征,想想记录过程也真是个漫长的工作。下面的行就是每张图片的标记了,第一列是图片名,后面的每个数字对应每一个特征,1表示正例,-1表示反例。

这样我们就有了图片和特征描述了,那怎么筛选出我们要的人脸图片呢?

处理标签

假设我们要把所有人脸分成戴了眼镜的和没戴眼镜的两份集合,来训练从戴眼镜到不戴眼镜的转换。一个个地人眼去看去分类显然是不现实的,描述文件的意义就在于此。

我们可以写一份Python代码来遍历txt中每一张图片对应的“Eyeglasses”属性列,看是不是1,从而判断对应图片是否戴了眼镜。

数一数可以知道“Eyeglasses”是第16个属性,这样,我们可以读取这份属性描述txt,遍历每一行,看对应列是否是1,从而将图片名筛分到两个txt中去:

f = open("list_attr_celeba.txt")
newTxt = "glass.txt"
newf = open(newTxt, "a+")
newNoTxt = "noGlass.txt"
newNof = open(newNoTxt, "a+")

line = f.readline() 
line = f.readline() 
line = f.readline() 
while line:
    array = line.split()
    if (array[0] == "000154.jpg"): print(array[16])

    if (array[16] == "-1"): 
        new_context = array[0] + '\n'
        newNof.write(new_context)
    else: 
        new_context = array[0] + '\n'
        newf.write(new_context)
    line = f.readline()

lines = len(newf.readlines()) 
print ("There are %d lines in %s" % (lines, newTxt)) 
lines = len(newNof.readlines()) 
print ("There are %d lines in %s" % (lines, newNoTxt))

f.close()
newf.close()
newNof.close()

如上代码,我们读取"list_attr_celeba.txt",并且创建两个新txt来分别存储有无戴眼镜的图片名。然后遍历每一行(跳过头两行),去判断眼镜属性是否是1,这里每一列用line.split()分隔开成数组,注意不能用line.split(" "),因为数字之间有时候是一个空格,有时候是两个空格,被坑了一下= =

当然你也可以像我一样先自己去图片集合中找一个戴了眼镜的图片,看看我们的代码是不是把对应的图片名下的1识别出来,我这里找的就是000154.jpg这张图来验证。

最后,我统计了一下有无戴眼镜的人脸的数量,结果是:

image.png

筛选图片

得到两个记录了有无戴眼镜的图片名集合txt后,我们就可以根据这个来筛选图片了。

图片共二十多万张,我们如果采用针对一个txt中每个图片名都去从头到尾到文件夹里找一次的方案,处理起来就太慢了。

这里我们采取更快速的方法,遍历文件夹中所有图片,对于遇到的每个图片名(当然,因为文件夹中不止图片,所以先判断是否是图片,也就是后缀是否是.jpg),去记录有无戴眼镜的两个txt中分别找是否包含该图片名,哪个包含,则把该图片移动到对应文件夹下去。

这里还可以优化的是,在txt中找图片名时,也不能每次要找都从头到尾找,而要记录上次找到的位置,因为图片名时按顺序排好的,所以下一张要找的图片名一定会在下面的行,而不会在之前出现过的行,这样也可以提速。

由于两个txt中行数不一致(有无戴眼镜的图片数量不同),所以要判断当一个txt全部找完后,之后就不要再去该txt中找了,更不要继续往后移动行,这样会出错的。

整体优化完成后代码如下:

import os
import shutil

nof = open("noGlass.txt")
hasf = open("glass.txt")

noLine = nof.readline() 
hasLine = hasf.readline() 

list = os.listdir("./")
hasGo = True
noGo = True
for i in range(0, len(list)):
    imgName = os.path.basename(list[i])
    if (os.path.splitext(imgName)[1] != ".jpg"): continue

    noArray = noLine.split()
    if (len(noArray) < 1): noGo = False
    hasArray = hasLine.split()
    if (len(hasArray) < 1): hasGo = False

    if (noGo and (imgName == noArray[0])):
        oldname= "./"+imgName
        newname="./noGlass/"+imgName
        shutil.move(oldname, newname)
        noLine = nof.readline()
    elif (hasGo and (imgName == hasArray[0])):
        oldname= "./"+imgName
        newname="./hasGlass/"+imgName
        shutil.move(oldname, newname)
        hasLine = hasf.readline()

    if (i % 100 == 0): print(imgName)

nof.close()
hasf.close()

“no”开头的对应未戴眼镜相关变量,“has”开头的对应戴了眼镜相关变量。思路就是上面说的几个优化的地方了。

这样一套处理二十多万张图片的筛选移动,总共花了不到一分钟。之前未优化时,处理了两个小时还只处理了一万多张,而且是越处理越慢,显而易见,每次都要从头找的话,越到后面,不必要的从头遍历条目越多。

方形脸部截取

虽然CelebA帮我们把人脸部分裁剪出来了,但由于我要处理的网络需要方形图片,也就是宽高相等的图片,所以这里再处理一遍:

from PIL import Image
import face_recognition
import os

# for (int i = 1; i <= 10; i++)
list = os.listdir("./")
for i in range(0, len(list)):
    imgName = os.path.basename(list[i])
    if (os.path.splitext(imgName)[1] != ".jpg"): continue

    image = face_recognition.load_image_file(imgName)

    face_locations = face_recognition.face_locations(image)

    for face_location in face_locations:

        # Print the location of each face in this image
        top, right, bottom, left = face_location
        # print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))

        # You can access the actual face itself like this:
        width = right - left
        height = bottom - top
        if (width > height):
            right -= (width - height)
        elif (height > width):
            bottom -= (height - width) 
        face_image = image[top:bottom, left:right]
        pil_image = Image.fromarray(face_image)
        pil_image.save('face%s'%imgName)

这份代码就是遍历文件夹中的所有图片,用face_recognition库去识别出人脸位置的上下左右坐标,基本识别得出的坐标就已经是方形的了,特殊情况下会有一个像素的误差,所以我强制对比了一次宽高,不一样就改成一样的,对裁剪的影响也不会很大。需注意的是要运行这份代码需要安装face_recognition库和PIL库,如何安装就可以直接搜索教程了。

这里我们就得到了所有高宽相等的人脸二次裁剪图片。

还要注意的一点是这里只保证了每张图片自身高宽相等,图片之间的尺寸并不一定是同样大小的。

这样,就完成了针对一个维度去做二位类处理筛选数据集的工作。


查看作者首页

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容