引
CalebA人臉數(shù)據(jù)集(官網(wǎng)鏈接)是香港中文大學(xué)的開放數(shù)據(jù)屎鳍,包含10,177個名人身份的202,599張人臉圖片蝎抽,并且都做好了特征標(biāo)記抬吟,這對人臉相關(guān)的訓(xùn)練是非常好用的數(shù)據(jù)集榴芳。
別看只是一堆人臉侦讨,他們很貼心地做好了特征標(biāo)記,也就是說儒士,你可以找到類似下面這些標(biāo)簽:
更貼心的是的止,他們除了Google盤的下載方式,還為國內(nèi)研究人員提供了百度網(wǎng)盤的下載鏈接着撩,這在他們官方都可以找到诅福。
不過剛進(jìn)入百度網(wǎng)盤的文件,可能有點(diǎn)迷拖叙,不知道什么文件是干嘛的氓润,也不知道怎么處理,尤其是對于新手來說薯鳍,這里就總結(jié)一下咖气。
文件含義
進(jìn)入百度網(wǎng)盤可以看到多個文件,但是都是干什么的呢挖滤?應(yīng)該下載哪個來用呢崩溪?
在動手下載之前,最好先讀讀README文件斩松,里面有比較詳細(xì)的描述伶唯,這里只簡單介紹一下:
- Img文件夾下是所有圖片,圖片又分三類文件:
其中“img_celeba.7z”文件夾是純“野生”文件惧盹,也就是從網(wǎng)絡(luò)爬取的沒有做裁剪的圖片乳幸,要解壓的話需要整個文件夾一起解壓;“img_align_celeba_png.7z”和“img_align_celeba.zip”是把“野生”文件裁剪出人臉部分之后的圖片钧椰,其中“img_align_celeba_png.7z”是png格式的粹断,比較大,也要整個文件夾一起解壓嫡霞,“img_align_celeba.zip”是jpg格式的瓶埋,比較小,1G多秒际,我采用的是這個文件悬赏,直接解壓即可。
不過需要注意的是里面的圖片并不是正方形的娄徊,所以如果你的網(wǎng)絡(luò)需要方形圖片輸入闽颇,自己還得處理一遍,后文有這部分的代碼寄锐。
圖中可以看到兵多,人臉圖片的名字只是簡單的編號尖啡,那膚色、發(fā)色剩膘、眼鏡衅斩、性別等特征標(biāo)簽在哪呢,在之前的“Anno”文件夾中:
第一個“l(fā)ist_attr_celeba.txt”文本文件就記錄了每一張圖片的特征標(biāo)簽:
如圖所示怠褐,第一行表示圖片個數(shù)畏梆,第二行是特征類型,以空格分開奈懒,可以看到有超多特征奠涌,想想記錄過程也真是個漫長的工作。下面的行就是每張圖片的標(biāo)記了磷杏,第一列是圖片名溜畅,后面的每個數(shù)字對應(yīng)每一個特征,1表示正例极祸,-1表示反例慈格。
這樣我們就有了圖片和特征描述了,那怎么篩選出我們要的人臉圖片呢遥金?
處理標(biāo)簽
假設(shè)我們要把所有人臉分成戴了眼鏡的和沒戴眼鏡的兩份集合浴捆,來訓(xùn)練從戴眼鏡到不戴眼鏡的轉(zhuǎn)換。一個個地人眼去看去分類顯然是不現(xiàn)實的汰规,描述文件的意義就在于此汤功。
我們可以寫一份Python代碼來遍歷txt中每一張圖片對應(yīng)的“Eyeglasses”屬性列,看是不是1溜哮,從而判斷對應(yīng)圖片是否戴了眼鏡。
數(shù)一數(shù)可以知道“Eyeglasses”是第16個屬性色解,這樣茂嗓,我們可以讀取這份屬性描述txt,遍歷每一行科阎,看對應(yīng)列是否是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"锣笨,并且創(chuàng)建兩個新txt來分別存儲有無戴眼鏡的圖片名蝌矛。然后遍歷每一行(跳過頭兩行),去判斷眼鏡屬性是否是1错英,這里每一列用line.split()分隔開成數(shù)組入撒,注意不能用line.split(" "),因為數(shù)字之間有時候是一個空格椭岩,有時候是兩個空格茅逮,被坑了一下= =
當(dāng)然你也可以像我一樣先自己去圖片集合中找一個戴了眼鏡的圖片璃赡,看看我們的代碼是不是把對應(yīng)的圖片名下的1識別出來,我這里找的就是000154.jpg這張圖來驗證献雅。
最后碉考,我統(tǒng)計了一下有無戴眼鏡的人臉的數(shù)量,結(jié)果是:
篩選圖片
得到兩個記錄了有無戴眼鏡的圖片名集合txt后挺身,我們就可以根據(jù)這個來篩選圖片了侯谁。
圖片共二十多萬張,我們?nèi)绻捎冕槍σ粋€txt中每個圖片名都去從頭到尾到文件夾里找一次的方案章钾,處理起來就太慢了墙贱。
這里我們采取更快速的方法,遍歷文件夾中所有圖片伍玖,對于遇到的每個圖片名(當(dāng)然嫩痰,因為文件夾中不止圖片,所以先判斷是否是圖片窍箍,也就是后綴是否是.jpg)串纺,去記錄有無戴眼鏡的兩個txt中分別找是否包含該圖片名,哪個包含椰棘,則把該圖片移動到對應(yīng)文件夾下去纺棺。
這里還可以優(yōu)化的是,在txt中找圖片名時邪狞,也不能每次要找都從頭到尾找祷蝌,而要記錄上次找到的位置,因為圖片名時按順序排好的巨朦,所以下一張要找的圖片名一定會在下面的行,而不會在之前出現(xiàn)過的行糊啡,這樣也可以提速吁津。
由于兩個txt中行數(shù)不一致(有無戴眼鏡的圖片數(shù)量不同)棚蓄,所以要判斷當(dāng)一個txt全部找完后,之后就不要再去該txt中找了碍脏,更不要繼續(xù)往后移動行,這樣會出錯的典尾。
整體優(yōu)化完成后代碼如下:
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”開頭的對應(yīng)未戴眼鏡相關(guān)變量,“has”開頭的對應(yīng)戴了眼鏡相關(guān)變量急黎。思路就是上面說的幾個優(yōu)化的地方了扎狱。
這樣一套處理二十多萬張圖片的篩選移動侧到,總共花了不到一分鐘淤击。之前未優(yōu)化時,處理了兩個小時還只處理了一萬多張污抬,而且是越處理越慢,顯而易見矢腻,每次都要從頭找的話射赛,越到后面多柑,不必要的從頭遍歷條目越多楣责。
方形臉部截取
雖然CelebA幫我們把人臉部分裁剪出來了,但由于我要處理的網(wǎng)絡(luò)需要方形圖片初嘹,也就是寬高相等的圖片沮趣,所以這里再處理一遍:
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庫去識別出人臉位置的上下左右坐標(biāo),基本識別得出的坐標(biāo)就已經(jīng)是方形的了缸匪,特殊情況下會有一個像素的誤差,所以我強(qiáng)制對比了一次寬高,不一樣就改成一樣的豌骏,對裁剪的影響也不會很大。需注意的是要運(yùn)行這份代碼需要安裝face_recognition庫和PIL庫窃躲,如何安裝就可以直接搜索教程了。
這里我們就得到了所有高寬相等的人臉二次裁剪圖片躁倒。
還要注意的一點(diǎn)是這里只保證了每張圖片自身高寬相等,圖片之間的尺寸并不一定是同樣大小的秧秉。
結(jié)
這樣,就完成了針對一個維度去做二位類處理篩選數(shù)據(jù)集的工作荧嵌。