人臉識別:insightface自定義數據集製作 | 附練手數據集
- 2019 年 11 月 24 日
- 筆記
來源:公眾號 AI演算法與影像處理 授權轉
01
人臉識別簡介
簡單來講,人臉識別這個問題,就是給定兩個人臉,然後判定他們是不是同一個人,這是它最原始的定義。它有很多應用場景,比如銀行櫃檯、海關、手機解鎖、酒店入住、網吧認證,會查身份證跟你是不是同一個人。
關於人臉識別的內容,網上資料很多,這裡推薦一篇綜述,詳細介紹了一些人臉識別的背景和目前的相關研究,以及常用的人臉識別模型:
http://www.elecfans.com/d/709424.html
好了,直接進入主題,今天的重點:
insightface論文:https://arxiv.org/abs/1801.07698
insightface github項目:https://github.com/deepinsight/insightface
02
製作數據集
一、環境配置
官方提供的項目是基於mxnet框架的
因此首先需要配置好這些環境,這裡假設已經安裝好cuda等
mxnet的安裝相對來說比較簡單(相對於caffe)
(1)查詢自己cuda的版本
# 輸入 nvcc -V # 輸出結果 nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2017 NVIDIA Corporation Built on Fri_Sep__1_21:08:03_CDT_2017 Cuda compilation tools, release 9.0, V9.0.176
(2)用pip安裝mxnet(GPU版本)
pip install mxnet-cu90 # 根據自身情況查詢對應的安裝命令,例如我伺服器的cuda版本是10.0的,安裝命令為 pip install mxnet-cu100
二、克隆項目和數據集下載
將insight項目克隆到本地
git clone --recursive https://github.com/deepinsight/insightface.git
下載lfw數據集
鏈接:http://vis-www.cs.umass.edu/lfw/index.html#download
這裡為了方便,提供了lfw的部分用於練手
練手數據集https://www.lanzous.com/i7gdxva,僅用於製作數據集練手
觀察數據集:

這裡提供了20個用於練手,完整數據集可以去上面的鏈接或自行查找下載。
說明:每個文件夾名為人的姓名,文件夾內包含多張人臉(>=1)。

三、開始製作所需要格式的數據集
(1)數據對齊並生成lst文件
將lfw數據集下載好並放置在datasets下(這裡以lfwdata命名的文件夾),然後新建一個文件夾並命名為output保存對齊後的人臉圖片,新建一個文件夾命名為train用於保存輸出結果
運行insightface項目下 src/align下的align_lfw.py文件
python align_lfw.py --input-dir ../../datasets/lfwdata --output-dir ../../datasets/output
–input :輸入需要對齊的數據的路徑
–output:輸出對齊後的數據保存路徑
對齊後的圖片

遇到問題1: ValueError: Object arrays cannot be loaded when allow_pickle=False 解決方案: pip3 install numpy==1.16.1 如果已安裝了多個numpy版本,需要先將其卸載後在運行 遇到問題2: AttributeError: module 'scipy.misc' has no attribute 'imread' 解決方案: pip install scipy==1.1.0
如果成功運行,output文件夾下會產生對齊後的人臉以及一個lst文件(將lst文件改名為 train.lst,並移動到train文件下)
終端下,移動和重命名的操作
# 重命名 mv [原始文件名] [改變後的文件名] mv lst train.lst # 移動 mv [起始文件路徑] [目標文件路徑] mv train.lst ../train/
觀察生成的 lst 文件內容:
1 ../../datasets/train/Abbas_Kiarostami/Abbas_Kiarostami_0001.jpg 0 1 ../../datasets/train/Abdel_Aziz_Al-Hakim/Abdel_Aziz_Al-Hakim_0001.jpg 1 # 其中1代表對齊,最後的數字0,1代表class label 中間是地址;需要用t表示tab鍵,不能用空格間隔。
(2)創建property配置文件
在datasets/train下創建property,沒有後綴
寫入下面內容,含義1000,112,112代表ID數量,尺寸,尺寸
1000,112,112
(3)生成rec&idx文件(依託於lst文件)
運行src/data face2rec2.py
python face2rec2.py ../../datasets/train/
運行可能會報錯,需要修改,可能原因是源程式碼是基於python2的
在python3下運行,修改第105行成如下所示:
s = mx.recordio.pack(header, b'')
運行成功後會出現兩個文件
train.idx和train.rec
將train.idx, train.rec是放置在train文件夾中,用於後續訓練使用,不然後續創建pair會報錯
(4)創建pair文件
為了做測試我們需要生成驗證集用的bin文件,bin文件生成前需要做pair文件,就是一對一對的數據,每一行分別是
圖A的目錄 空格 圖B的目錄 空格 標誌0/1(代表兩張圖類別一致否)
在src/data下新建一個程式碼generate_image_pairs.py用於生成pairs
程式碼來源:https://blog.csdn.net/CLOUD_J/article/details/100672392
# coding:utf-8 import sys import os import random import time import itertools import pdb import argparse #src = '../../datasets/lfw2' #dst = open('../../datasets/lfw/train.txt', 'a') parser = argparse.ArgumentParser(description='generate image pairs') # general parser.add_argument('--data-dir', default='', help='') parser.add_argument('--outputtxt', default='', help='path to save.') parser.add_argument('--num-samepairs',default=100) args = parser.parse_args() cnt = 0 same_list = [] diff_list = [] list1 = [] list2 = [] folders_1 = os.listdir(args.data_dir) dst = open(args.outputtxt, 'a') count = 0 dst.writelines('n') # 產生相同的影像對 for folder in folders_1: sublist = [] same_list = [] imgs = os.listdir(os.path.join(args.data_dir, folder)) for img in imgs: img_root_path = os.path.join(args.data_dir, folder, img) sublist.append(img_root_path) list1.append(img_root_path) for item in itertools.combinations(sublist, 2): for name in item: same_list.append(name) if len(same_list) > 10 and len(same_list) < 13: for j in range(0, len(same_list), 2): if count < int(args.num_samepairs):#數量可以修改 dst.writelines(same_list[j] + ' ' + same_list[j+1]+ ' ' + '1' + 'n') count += 1 if count >= int(args.num_samepairs): break list2 = list1.copy() # 產生不同的影像對 diff = 0 print(count) # 如果不同的影像對遠遠小於相同的影像對,則繼續重複產生,直到兩者相差很小 while True: random.seed(time.time() * 100000 % 10000) random.shuffle(list2) for p in range(0, len(list2) - 1, 2): if list2[p] != list2[p + 1]: dst.writelines(list2[p] + ' ' + list2[p + 1] + ' ' + '0'+ 'n') diff += 1 if diff >= count: break #print(diff) if diff < count: #print('--') continue else: break
運行generate_image_pairs.py
python3 generate_image_pairs.py --data-dir ../../datasets/output --outputtxt ../../datasets/train/train.txt --num-samepairs 5
–data-dir 後接對齊後的人臉
–outputtxt 用於保存train.txt文件
–num-samepairs 生成多少對(具體如何設置,還需要好好研究一下)
運行成功後在datasets/train下會生成一個train.txt文件
內容:
../../datasets/output/Abdullah/Abdullah_0002.jpg ../../datasets/output/Abdullah/Abdullah_0004.jpg 1 # 前面兩個分布是生成的pairs的路徑,後面的0/1代表是否同一個人或類
(5)生成驗證集bin文件
成功後利用/src/data/下的 lfw2pack.py生成bin文件
但是存在點問題,對lfw2pack.py進行稍微的修改,修改lfw2pack.py中19行,打#的為更改的,改為兩個參數,一個是txt讀出來的列表,另一個是總數量。
import mxnet as mx from mxnet import ndarray as nd import argparse import pickle import sys import os sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'eval')) import lfw parser = argparse.ArgumentParser(description='Package LFW images') # general parser.add_argument('--data-dir', default='', help='') # 修改1:影像大小修改為112,112 parser.add_argument('--image-size', type=str, default='112,112', help='') parser.add_argument('--output', default='', help='path to save.') # 修改2:添加解析參數 parser.add_argument('--num-samepairs',default=100) args = parser.parse_args() lfw_dir = args.data_dir image_size = [int(x) for x in args.image_size.split(',')] # 修改3:將文件名pairs.txt修改成train.txt lfw_pairs = lfw.read_pairs(os.path.join(lfw_dir, 'train.txt')) print(lfw_pairs) # 修改4:下一行進行修改成需要的格式 # lfw_paths, issame_list = lfw.get_paths(lfw_dir, lfw_pairs, 'jpg') lfw_paths, issame_list = lfw.get_paths(lfw_pairs,int(args.num_samepairs)+1)#, 'jpg') lfw_bins = [] #lfw_data = nd.empty((len(lfw_paths), 3, image_size[0], image_size[1])) print(len(issame_list)) i = 0 for path in lfw_paths: with open(path, 'rb') as fin: _bin = fin.read() lfw_bins.append(_bin) #img = mx.image.imdecode(_bin) #img = nd.transpose(img, axes=(2, 0, 1)) #lfw_data[i][:] = img i+=1 if i%1000==0: print('loading lfw', i) with open(args.output, 'wb') as f: pickle.dump((lfw_bins, issame_list), f, protocol=pickle.HIGHEST_PROTOCOL)
對應的get_paths這個文件存在src/eval/lfw.py下,把它也改了
def get_paths(pairs, same_pairs): nrof_skipped_pairs = 0 path_list = [] issame_list = [] cnt = 1 for pair in pairs: path0 = pair[0] path1 = pair[1] if cnt < same_pairs: issame = True else: issame = False if os.path.exists(path0) and os.path.exists(path1): # Only add the pair if both paths exist path_list += (path0,path1) issame_list.append(issame) else: print('not exists', path0, path1) nrof_skipped_pairs += 1 cnt += 1 if nrof_skipped_pairs>0: print('Skipped %d image pairs' % nrof_skipped_pairs) return path_list, issame_list
vim中多行注釋方法:
多行注釋: 1. 進入命令行模式,按ctrl + v進入 visual block模式,然後按j, 或者k選中多行,把需要注釋的行標記起來 2. 按大寫字母I,再插入注釋符,例如 # 3. 按esc鍵就會全部注釋了 取消多行注釋: 1. 進入命令行模式,按ctrl + v進入 visual block模式,按字母l橫向選中列的個數,例如 # 需要選中2列 2. 按字母j,或者k選中注釋符號 3. 按d鍵就可全部取消注釋
之後再運行
python3 lfw2pack.py --data-dir ../../datasets/train --output ../../datasets/train/lfw.bin --num-samepairs 2
注意:我這裡報錯了(沒有報錯的小夥伴可以忽略)
path0 = pair[0] IndexError: list index out of range
仔細分析之後,是因為在train.txt中存在空白行導致,直接刪除即可,如果沒有報錯可以直接忽略
至此,我們已經完成了數據集的製作,後續會更新如何訓練,以及使用。
這部分內容,是本人摸索了很久才搞定的,本文儘可能的寫的詳細,希望能幫到大家,寫這篇的時候又重新操作了一遍!
參考鏈接: https://blog.csdn.net/CLOUD_J/article/details/100672392 https://blog.csdn.net/hanjiangxue_wei/article/details/86566497