├── CAMFA.py ├── CAMFA.pyc ├── FaceAlignment.ipynb ├── README.md ├── dataset ├── CAM300W_test.pkl └── CAM300W_train.pkl ├── expdata ├── face1.png ├── face2.png ├── face3.png ├── fig_confmatrix.txt ├── fig_ots_basic.pkl ├── fig_ots_center_auc02_new.txt └── fig_ots_scale_auc02_new.txt ├── facedraw.py ├── facedraw.pyc └── result.txt /CAMFA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | This file is used to analyze the error of face alignment. 4 | By Heng Yang yanghengnudt@gmail.com 5 | Computer Laboratory 6 | University of Cambridge 7 | Jun. 2015 8 | ''' 9 | 10 | import numpy as np 11 | 12 | #for visualisation we need some additional libs 13 | import pylab as plt 14 | import prettyplotlib as ppl 15 | def plot_rect(rect,ltype='g-'): 16 | ppl.plot([rect[0],rect[0]+rect[2],rect[0]+rect[2],rect[0],rect[0]],[rect[1],rect[1],rect[1]+rect[3],rect[1]+rect[3],rect[1]],ltype,lw=3) 17 | def show_one_image(img,bb,lms): 18 | plt.imshow(img) 19 | plot_rect(bb) 20 | ppl.plot(lms[0::2],lms[1::2],'.') 21 | plt.axis('off') 22 | 23 | ##end of visualisation code 24 | 25 | def cal_iod_300W(gtxy): 26 | ''' 27 | this function calculates inter-ocular distance given the ground truth 28 | facial landmark locations. If the data set is different from 300W 29 | please write your own function for this purpose. 30 | ''' 31 | olx = gtxy[:,36*2] 32 | oly = gtxy[:,36*2+1] 33 | for idx in np.arange(37,42): 34 | olx += gtxy[:,idx*2] 35 | oly += gtxy[:,idx*2+1] 36 | oly /= 6. 37 | olx /= 6. 38 | orx = gtxy[:,42*2] 39 | ory = gtxy[:,42*2+1] 40 | for idx in np.arange(43,48): 41 | orx += gtxy[:,idx*2] 42 | ory += gtxy[:,idx*2+1] 43 | ory /= 6. 44 | orx /= 6. 45 | dioxy = np.array([olx-orx])**2+np.array([oly-ory])**2 46 | diod1 = np.sqrt(dioxy) 47 | return diod1 48 | 49 | 50 | class Group_Error_ANA(): 51 | def __init__(self, gtxy, database='300W'): 52 | ''' database variable is used to indicate how to calculate the 53 | inter-ocular distance 54 | initialization with ground truth locations 55 | ''' 56 | self.gtxy = gtxy 57 | self.nerr = [] 58 | if database is '300W': 59 | self.iod1 = cal_iod_300W(self.gtxy) 60 | else: 61 | print ("You need to define how to calculate IOD for other databases") 62 | raise 63 | def get_norm_err(self,xy): 64 | ''' 65 | calculate normalised error of each facial landmark 66 | ''' 67 | if not xy.shape[0] == self.gtxy.shape[0]: 68 | return ("inconsistent number of samples") 69 | num_p = xy.shape[1]# It accepts 68, 66 and 49 points 70 | gtxy_ = self.gtxy 71 | if num_p == 98: 72 | gtxy_ = np.c_[gtxy_[:,34:120],gtxy_[:,122:128],gtxy_[:,130:136]] 73 | elif num_p == 132: 74 | gtxy_ = np.delete(gtxy_,[60*2,60*2+1,128,129],1)# two inner mouth corners are excluded 75 | diod = np.tile(self.iod1.T,(1,num_p/2)) 76 | err = gtxy_ - xy 77 | err = err[:,0::2]**2+err[:,1::2]**2 78 | nerr = np.sqrt(err)/diod 79 | return nerr 80 | def get_edf_histogram(self,xy, thr_ = 0.3): 81 | ''' 82 | calculate the popupar error distribution function curve given a threthold 83 | it returns the curve as well as the area under the curve 84 | ''' 85 | self.nerr = self.get_norm_err(xy) 86 | num_ps = float(self.nerr.flatten().shape[0]) 87 | hist,bin_edges = np.histogram(self.nerr.flatten(),bins=5000,range=(0,thr_)) 88 | CDF = np.insert(np.cumsum(hist)/num_ps,0,0) 89 | area = np.sum(CDF[1:]*np.diff(bin_edges)) 90 | return area, bin_edges, CDF 91 | def get_det_rate(self,xy,thr_ = 0.1): 92 | ''' 93 | calculate the detection rate, i.e. the percentage of landmarks localised within a 94 | given threthold 95 | ''' 96 | self.nerr = self.get_norm_err(xy) 97 | return np.sum(self.nerr.flatten() < thr_)/float(self.nerr.flatten().shape[0]) 98 | 99 | '''application examples using the saved database''' 100 | if __name__ == '__main__': 101 | import pandas as pd 102 | import pylab as plt 103 | DB = pd.read_pickle('./dataset/DB_test_with_pose.pkl') 104 | gtxy = np.array([xy for xy in DB['GTxys'].values])# get ground truth xy 105 | GEA = Group_Error_ANA(gtxy[0::2])# initialization of the class member 106 | xy = np.loadtxt('/home/hy306/Desktop/CoolFace/BB_IBUG_EoT_result.txt') 107 | area,bins,cdf = GEA.get_edf_histogram(xy,0.2) 108 | plt.plot(bins,cdf) 109 | print ('Area under the curve is: {}'.format(area)) 110 | plt.show() 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /CAMFA.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisYang/FaceAlignmentCompare/751f98ff71e687d91bdf0cf67ac8b1aa93a5882a/CAMFA.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Face AlignmentEvaluation 2 | This is the data and code to generate the results in the technique report "An Empirical Study of Recent Face Alignment Methods" 3 | 4 | http://personal.ie.cuhk.edu.hk/~ccloy/project_face_alignment/index.html 5 | -------------------------------------------------------------------------------- /expdata/face1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisYang/FaceAlignmentCompare/751f98ff71e687d91bdf0cf67ac8b1aa93a5882a/expdata/face1.png -------------------------------------------------------------------------------- /expdata/face2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisYang/FaceAlignmentCompare/751f98ff71e687d91bdf0cf67ac8b1aa93a5882a/expdata/face2.png -------------------------------------------------------------------------------- /expdata/face3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisYang/FaceAlignmentCompare/751f98ff71e687d91bdf0cf67ac8b1aa93a5882a/expdata/face3.png -------------------------------------------------------------------------------- /expdata/fig_confmatrix.txt: -------------------------------------------------------------------------------- 1 | 1.282206531204644340e-01 1.432880875949799626e-01 1.492882438316400495e-01 1.346569734483052949e-01 2 | 8.236261722105388261e-02 1.327305127217795755e-01 1.325000000000000067e-01 1.112828162672906684e-01 3 | 1.014618654486467986e-01 1.402163382566379290e-01 1.401000000000000023e-01 1.300218859387005932e-01 4 | 1.223128429949628670e-01 1.047164253393665229e-01 1.180078032954836598e-01 1.188787723042773192e-01 5 | 1.452018278835481846e-01 4.816504140698369546e-02 9.244388628020148990e-02 1.108765534022026766e-01 6 | 1.159326050768638476e-01 1.007038891028109312e-02 7.174557507182845184e-02 8.635125973756702100e-02 7 | 1.230579425964870854e-01 1.308254518527294796e-01 1.401546814371612271e-01 1.263208696424868993e-01 8 | 1.294522974472808174e-01 4.834422863485016153e-02 9.722079313583198434e-02 1.188425074703321205e-01 9 | 1.333163143515751760e-01 1.058265807222743826e-01 1.136999999999999955e-01 1.060126850507982776e-01 10 | 6.079621234111800415e-02 1.109649997800941090e-01 1.097742472621717896e-01 8.032112767735409553e-02 11 | 1.395031281482114194e-01 8.989543925552806458e-02 1.231646760010245006e-01 1.340104610262101981e-01 12 | -------------------------------------------------------------------------------- /expdata/fig_ots_center_auc02_new.txt: -------------------------------------------------------------------------------- 1 | 1.492882438316400495e-01 1.494700322718346963e-01 1.491591896183727672e-01 1.482776930760693335e-01 1.459268706565354790e-01 1.422936573038504349e-01 1.365781524801502611e-01 1.267009606420216872e-01 1.134877258601553890e-01 9.714330154529156325e-02 7.735259694356698412e-02 5.690389464697345051e-02 2 | 1.327305127217795755e-01 1.324650235478807003e-01 1.321346136666567972e-01 1.317445801960842322e-01 1.309305899706762477e-01 1.299915953911317812e-01 1.290698745890228594e-01 1.265719922988063351e-01 1.233403585201860164e-01 1.174381669974230713e-01 1.026272745475548948e-01 7.355564906252777535e-02 3 | 1.402163382566379290e-01 1.403343200000000124e-01 1.394747396909417070e-01 1.388708718517886187e-01 1.380463321096217777e-01 1.368627908307009089e-01 1.354164525740630121e-01 1.318921659694357040e-01 1.284483761632374266e-01 1.216543885426449451e-01 1.093722602236830987e-01 9.047819653376591720e-02 4 | 1.223128429949628670e-01 1.217605866985400909e-01 1.216599109536412870e-01 1.208405043968240333e-01 1.193296071885938947e-01 1.172053017160420013e-01 1.142020759839494637e-01 1.110439897549731214e-01 1.065564623922137938e-01 1.010436789891573373e-01 9.229899769486896166e-02 8.110502971057799437e-02 5 | 1.452018278835481846e-01 1.436989998292495518e-01 1.378758985742337961e-01 1.302051067190301281e-01 1.212704536839409181e-01 1.092445039699479342e-01 9.761546666097498104e-02 8.322113122171945210e-02 7.063124349013916170e-02 5.561666148723639486e-02 4.367752898488859448e-02 3.584015546828310528e-02 6 | 1.159851153698054083e-01 1.151989980154616289e-01 1.142269416190278830e-01 1.106963219098960477e-01 1.050953904208998535e-01 9.657982749326146643e-02 8.758729836201535612e-02 7.926416279138652488e-02 6.837533734190338885e-02 5.792534450993750555e-02 4.581678327063772732e-02 3.430319232250229888e-02 7 | 1.401546814371612271e-01 1.403343117798643569e-01 1.399361575782707978e-01 1.385742737478155295e-01 1.367574057640472962e-01 1.332049837386333480e-01 1.289398391042919367e-01 1.229004849382423492e-01 1.137607395515535941e-01 1.021675887562572271e-01 8.760720594769112746e-02 7.085088427475488837e-02 8 | 1.294522974472808174e-01 1.293860321010842640e-01 1.286219401519678729e-01 1.270407939042089984e-01 1.248565627934773437e-01 1.211386928199436747e-01 1.155451753607103366e-01 1.078762637240672645e-01 9.730366746350208040e-02 8.569988047468624082e-02 7.369754179117221637e-02 6.014936301545291408e-02 9 | 0.13336053701015965 0.13318396653291215 0.13288982071202937 0.13178924955178009 0.12983374455732949 0.12802865277896353 0.12145615043114488 0.116 0.11510406044565866 0.10789252369162469 0.096689462989840352 0.082583718091009992 10 | 1.395031281482114194e-01 1.396317327755485227e-01 1.393103549901818505e-01 1.388228738154187736e-01 1.379986348501664739e-01 1.363506178604969032e-01 1.340800965593784699e-01 1.326359811320755033e-01 1.236354679416033359e-01 1.221776300691539419e-01 1.009468433364637541e-01 9.635233347562538464e-02 11 | 1.109649997800941090e-01 1.102320701939569908e-01 1.100563495623873012e-01 1.087137045344592629e-01 1.079663816686458333e-01 1.066057052381580716e-01 1.033203747196200090e-01 9.962054096846550966e-02 9.196837401592117267e-02 7.528097902097902194e-02 5.572391960241017406e-02 3.770352992919030755e-02 12 | -------------------------------------------------------------------------------- /expdata/fig_ots_scale_auc02_new.txt: -------------------------------------------------------------------------------- 1 | 1.352183360368821030e-01 1.410219448476052462e-01 1.454943703577221970e-01 1.483366498762059282e-01 1.493026355331682764e-01 1.495339340903270031e-01 1.491738760351746240e-01 1.489872961666524331e-01 1.484173806881243229e-01 1.471007487407154368e-01 1.450260650559207776e-01 2 | 1.287832978880957269e-01 1.297617369153757361e-01 1.307940037321169424e-01 1.317842599449068619e-01 1.320483800835283161e-01 1.326982174698616845e-01 1.329245780634459861e-01 1.331867266964841257e-01 1.329818595420751981e-01 1.329123402742809767e-01 1.324568810165575594e-01 3 | 1.374245564757107241e-01 1.393345001280628381e-01 1.397875292410142578e-01 1.407214650388457500e-01 1.401699991462477535e-01 1.403551856911124385e-01 1.396153957141637592e-01 1.390312140356868564e-01 1.375545940408093581e-01 1.365913241697259695e-01 1.354162597114317579e-01 4 | 1.140292085716725079e-01 1.188229001963630183e-01 1.203341372833603706e-01 1.215504260223683186e-01 1.217326730982668803e-01 1.224441005720140058e-01 1.224523324511226918e-01 1.217469435669768646e-01 1.223300145137880962e-01 1.217403824810040225e-01 1.213435840519081349e-01 5 | 7.716328950738496339e-02 9.663641850934859279e-02 1.142090036711346512e-01 1.275216759156492796e-01 1.382398565696235093e-01 1.452017083582344548e-01 1.440361410398702247e-01 1.409919849739605657e-01 1.347370827285921946e-01 1.261441099632886476e-01 1.150839964142405941e-01 6 | 9.142271141257665712e-02 1.004211297058736352e-01 1.058445851722401715e-01 1.103724214330144282e-01 1.133774520896892873e-01 1.151320932436835365e-01 1.157190059536151172e-01 1.157514433814164218e-01 1.149169218921240465e-01 1.118851941589407983e-01 1.083878960931252133e-01 7 | 1.338410295903557368e-01 1.384033766772311269e-01 1.407208506857024477e-01 1.415848250940434094e-01 1.412186842806789078e-01 1.400993193329581610e-01 1.387024365392020564e-01 1.361246692929711788e-01 1.318262504072746921e-01 1.268831195758419739e-01 1.210826492106276547e-01 8 | 9.728773584905660299e-02 1.123707299581661462e-01 1.218744360966447643e-01 1.270678493981046708e-01 1.295902680782037020e-01 1.294486280201485595e-01 1.255290873388542616e-01 1.180162281225988208e-01 1.077159421155980540e-01 9.342031332707248503e-02 7.622839409203449601e-02 9 | 0.11462172799453602 0.12266608469222233 0.1279515307777683 0.13133501920942542 0.13300316656706224 0.13336053701015965 0.13208136173482454 0.12954568086741228 0.12567669427132247 0.12081195765388884 0.11482770255271921 10 | 9.446871003210627338e-02 1.021644350617935543e-01 1.064830179883010097e-01 1.087896705809913434e-01 1.093281224435941612e-01 1.104495430355807717e-01 1.111255873686062268e-01 1.105377754321150707e-01 1.102559625280380151e-01 1.097465875005497749e-01 1.087140405506443336e-01 11 | 1.324772688465807313e-01 1.380003286946128171e-01 1.401634107402032203e-01 1.405523887987706055e-01 1.404055288995133544e-01 1.394190463587466899e-01 1.378157935627081221e-01 1.356159352855801359e-01 1.325083343293776139e-01 1.274236455220695008e-01 1.212083155468283158e-01 12 | -------------------------------------------------------------------------------- /facedraw.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import pylab as plt 4 | import CAMFA as CF ##this is the python class used for evaluation 5 | import prettyplotlib as ppl 6 | import matplotlib 7 | import pickle as pkl 8 | def basic_compare(): 9 | matplotlib.rcParams['figure.figsize'] = (8, 5) 10 | fig1,ax1 = plt.subplots(1) 11 | colormap = plt.cm.spectral 12 | plt.gca().set_color_cycle([colormap(i) for i in np.linspace(0, 0.99, 11)]) 13 | with open('expdata/fig_ots_basic.pkl','rb') as fp: 14 | area2s,meanerrs,merrs_ecd10,curves = pkl.load(fp) 15 | figs = list() 16 | labels = ['TREES', 'CFAN', 'RCPR', 'IFA', 'CFSS', 'SDM', 'LBF', 'TCDCN', 'CCNF', 'GNDPM', 'DRMF'] 17 | for bins,cdf in curves: 18 | p, = ppl.plot(bins,cdf,lw=2) 19 | figs.append(p) 20 | 21 | s_ids = np.argsort(-area2s) 22 | figs = [figs[i] for i in s_ids] 23 | labels = [labels[i] for i in s_ids] 24 | plt.legend(figs, labels, loc=4, ncol=2) 25 | plt.xlabel('Normalised error') 26 | ax1.set_title('(a)') 27 | plt.ylabel('Proportion of facial landmarks') 28 | plt.grid() 29 | fig2,ax2 = plt.subplots(1) 30 | anno_area2s = [('%1.5f'%a)[0:6] for a in area2s[s_ids]] 31 | ppl.bar(ax2, np.arange(len(area2s)), area2s[s_ids], annotate=anno_area2s, grid='y',xticklabels=labels) 32 | plt.ylim((0.1,0.16)) 33 | plt.xlim((-0.06,11.02)) 34 | plt.xticks(rotation=25) 35 | ax2.set_title('(b)') 36 | 37 | ax2.set_ylabel('AUC$_{0.2}$') 38 | fig3,ax3 = plt.subplots(1) 39 | anno_me = ['%1.2f'%a for a in meanerrs[s_ids]*100] 40 | ppl.bar(ax3, np.arange(len(area2s)), meanerrs[s_ids]*100, annotate=anno_me, grid='y',xticklabels=labels) 41 | plt.xlim((-0.06,11.02)) 42 | plt.xticks(rotation=30) 43 | ax3.set_ylabel('Overall normalised mean error (%)') 44 | ax3.set_title('(c)') 45 | 46 | fig4,ax4 = plt.subplots(1) 47 | anno_me2 = ['%1.2f'%a for a in merrs_ecd10[s_ids]*100] 48 | ppl.bar(ax4, np.arange(len(area2s)), merrs_ecd10[s_ids]*100, annotate=anno_me2, grid='y',xticklabels=labels) 49 | plt.xlim((-0.06,11.02)) 50 | plt.xticks(rotation=30) 51 | ax4.set_ylabel('Overall normalised mean error (%)') 52 | ax4.set_title('(d)') 53 | 54 | def draw_ots_sens_center(): 55 | exam2 = plt.imread('expdata/face2.png') 56 | plt.imshow(exam2) 57 | t = plt.axis('off') 58 | plt.subplots(1) 59 | rs = np.arange(0.01,0.22,0.02) 60 | mcnew = np.loadtxt('expdata/fig_ots_center_auc02_new.txt') 61 | rs2 = np.insert(rs,0,0) 62 | s_ids = np.argsort(-mcnew[:,0]) 63 | colormap = plt.cm.spectral#rainbow 64 | plt.gca().set_color_cycle([colormap(i) for i in np.linspace(0, 0.99, 11)]) 65 | labels = ['TREES','SDM','RCPR','CCNF','CFAN','GNDPM','IFA','LBF','TCDCN','CFSS','DRMF'] 66 | for s in s_ids: 67 | ppl.plot(rs2,mcnew[s],lw=2, marker='o',label = labels[s],alpha=0.8) 68 | # ppl.plot(mc[-1],lw=2, marker='o',label='DRMF') 69 | plt.xlabel('Face centre shift') 70 | plt.ylabel('AUC$_{0.2}$') 71 | plt.legend(ncol=2,loc=3) 72 | plt.xlim((0,0.21)) 73 | plt.ylim((0.025,0.152)) 74 | plt.grid() 75 | def draw_ots_sens_scale(): 76 | all_results_scale = np.loadtxt('expdata/fig_ots_scale_auc02_new.txt') 77 | exam3 = plt.imread('expdata/face3.png') 78 | plt.imshow(exam3) 79 | t = plt.axis('off') 80 | plt.subplots(1) 81 | rs = np.arange(0.8,1.22,0.04) 82 | s_ids = np.argsort(-all_results_scale[:,5]) 83 | colormap = plt.cm.spectral#rainbow 84 | plt.gca().set_color_cycle([colormap(i) for i in np.linspace(0, 0.99, 11)]) 85 | labels = ['TREES','SDM','RCPR','CCNF','CFAN','GNDPM','IFA','LBF','TCDCN','DRMF','CFSS'] 86 | for s in s_ids: 87 | ppl.plot(rs,all_results_scale[s],lw=2, marker='o',label = labels[s],alpha=0.8) 88 | ppl.plot([1,1],[0.15,0.078],c=[0.5,0.5,0.5],alpha=0.5,lw=3) 89 | plt.xlabel('Scales') 90 | plt.ylabel('AUC$_{0.2}$') 91 | plt.legend(ncol=2,loc=8) 92 | plt.xlim((0.8,1.2)) 93 | plt.grid() 94 | def draw_real_facebb_res(): 95 | from prettyplotlib import brewer2mpl 96 | from matplotlib.colors import LogNorm 97 | red_purple = brewer2mpl.get_map('RdPu', 'Sequential', 9).mpl_colormap 98 | names = ['TREES', 'CFAN', 'RCPR', 'IFA', 'CFSS', 'SDM', 'LBF', 'TCDCN', 'CCNF', 'GNDPM', 'DRMF','CFSS'] 99 | bbsname = ['IBUG','V&J','HOG+SVM','HeadHunter'] 100 | mat = np.loadtxt('expdata/fig_confmatrix.txt') 101 | fig, ax = plt.subplots(1) 102 | ppl.pcolormesh(fig, ax, mat.T,xticklabels=names,yticklabels=bbsname) 103 | bestbbpairs = [2,1,1,0,0,0,2,0,0,1,0] 104 | for k in range(len(bbsname)): 105 | for k2 in range(11): 106 | if bestbbpairs[k2] == k: 107 | ax.annotate(('%1.5f'%mat[k2][k])[:6],xy=(k2+0.5,k+0.5),bbox=dict(boxstyle="round", fc="cyan",alpha=0.8), 108 | horizontalalignment='center', 109 | verticalalignment='center') 110 | else: 111 | ax.annotate(('%1.5f'%mat[k2][k])[:6],xy=(k2+0.5,k+0.5), 112 | horizontalalignment='center', 113 | verticalalignment='center') 114 | plt.xlim((0,11)) 115 | -------------------------------------------------------------------------------- /facedraw.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisYang/FaceAlignmentCompare/751f98ff71e687d91bdf0cf67ac8b1aa93a5882a/facedraw.pyc --------------------------------------------------------------------------------