Coverage for src/bob/bio/face/database/gbu.py: 31%

114 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-07-13 00:04 +0200

1#!/usr/bin/env python 

2# vim: set fileencoding=utf-8 : 

3# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> 

4# Sat 20 Aug 15:43:10 CEST 2016 

5 

6import os 

7import xml.sax 

8 

9from functools import partial 

10 

11from clapper.rc import UserDefaults 

12 

13import bob.io.base 

14 

15from bob.bio.base.database.utils import download_file, md5_hash, search_and_open 

16from bob.bio.base.pipelines.abstract_classes import Database 

17from bob.pipelines import DelayedSample, SampleSet 

18 

19rc = UserDefaults("bobrc.toml") 

20 

21""" 

22GBU Database 

23 

24Several of the rules used in this code were imported from 

25https://gitlab.idiap.ch/bob/bob.db.gbu/-/blob/master/bob/db/gbu/create.py 

26""" 

27 

28 

29def load_annotations(annotations_file): 

30 annotations = dict() 

31 for i, line in enumerate(annotations_file.readlines()): 

32 # Skip the first line 

33 if i == 0: 

34 continue 

35 line = line.split(",") 

36 path = os.path.splitext(os.path.basename(line[0]))[0] 

37 annotations[path] = { 

38 "leye": (float(line[-1]), float(line[-2])), 

39 "reye": (float(line[2]), float(line[1])), 

40 } 

41 return annotations 

42 

43 

44class File(object): 

45 def __init__(self, subject_id, template_id, path): 

46 self.subject_id = subject_id 

47 self.template_id = template_id 

48 self.path = path 

49 

50 

51class XmlFileReader(xml.sax.handler.ContentHandler): 

52 def __init__(self): 

53 self.m_signature = None 

54 self.m_path = None 

55 self.m_presentation = None 

56 self.m_file_list = dict() 

57 

58 def startDocument(self): 

59 pass 

60 

61 def endDocument(self): 

62 pass 

63 

64 def startElement(self, name, attrs): 

65 if name == "biometric-signature": 

66 self.m_signature = attrs["name"] # subject_id 

67 elif name == "presentation": 

68 self.m_path = os.path.splitext(attrs["file-name"])[0] # path 

69 self.m_presentation = attrs["name"] # template_id 

70 else: 

71 pass 

72 

73 def endElement(self, name): 

74 if name == "biometric-signature": 

75 # assert that everything was read correctly 

76 assert ( 

77 self.m_signature is not None 

78 and self.m_path is not None 

79 and self.m_presentation is not None 

80 ) 

81 # add a file to the sessions 

82 self.m_file_list[self.m_presentation] = File( 

83 subject_id_from_signature(self.m_signature), 

84 self.m_presentation, 

85 self.m_path, 

86 ) 

87 

88 self.m_presentation = self.m_signature = self.m_path = None 

89 else: 

90 pass 

91 

92 

93def subject_id_from_signature(signature): 

94 return int(signature[4:]) 

95 

96 

97def read_list(xml_file, eye_file=None): 

98 """Reads the xml list and attaches the eye files, if given""" 

99 # create xml reading instance 

100 handler = XmlFileReader() 

101 xml.sax.parse(xml_file, handler) 

102 return handler.m_file_list 

103 

104 

105class GBUDatabase(Database): 

106 """ 

107 The GBU (Good, Bad and Ugly) database consists of parts of the MBGC-V1 image set. 

108 It defines three protocols, i.e., `Good`, `Bad` and `Ugly` for which different model and probe images are used. 

109 

110 

111 .. warning:: 

112 

113 To use this dataset protocol, you need to have the original files of the IJBC datasets. 

114 Once you have it downloaded, please run the following command to set the path for Bob 

115 

116 .. code-block:: sh 

117 

118 bob config set bob.bio.face.gbu.directory [GBU PATH] 

119 

120 

121 The code below allows you to fetch the gallery and probes of the "Good" protocol. 

122 

123 .. code-block:: python 

124 

125 >>> from bob.bio.face.database import GBUDatabase 

126 >>> gbu = GBUDatabase(protocol="Good") 

127 >>> 

128 >>> # Fetching the gallery 

129 >>> references = gbu.references() 

130 >>> # Fetching the probes 

131 >>> probes = gbu.probes() 

132 

133 

134 """ 

135 

136 def __init__( 

137 self, 

138 protocol, 

139 annotation_type="eyes-center", 

140 fixed_positions=None, 

141 original_directory=rc.get("bob.bio.face.gbu.directory"), 

142 extension=rc.get("bob.bio.face.gbu.extension", ".jpg"), 

143 ): 

144 import warnings 

145 

146 warnings.warn( 

147 "The GBU database is not yet adapted to this version of bob. Please port it or ask for it to be ported.", 

148 DeprecationWarning, 

149 ) 

150 

151 # Downloading model if not exists 

152 urls = GBUDatabase.urls() 

153 self.filename = download_file( 

154 urls=urls, 

155 destination_filename="gbu-xmls.tar.gz", 

156 checksum="827de43434ee84020c6a949ece5e4a4d", 

157 checksum_fct=md5_hash, 

158 ) 

159 

160 self.references_dict = {} 

161 self.probes_dict = {} 

162 

163 self.annotations = None 

164 self.original_directory = original_directory 

165 self.extension = extension 

166 

167 self.background_samples = None 

168 self._background_files = [ 

169 "GBU_Training_Uncontrolledx1.xml", 

170 "GBU_Training_Uncontrolledx2.xml", 

171 "GBU_Training_Uncontrolledx4.xml", 

172 "GBU_Training_Uncontrolledx8.xml", 

173 ] 

174 

175 super().__init__( 

176 name="gbu", 

177 protocol=protocol, 

178 score_all_vs_all=True, 

179 annotation_type=annotation_type, 

180 fixed_positions=fixed_positions, 

181 memory_demanding=True, 

182 ) 

183 

184 @staticmethod 

185 def protocols(): 

186 return ["Good", "Bad", "Ugly"] 

187 

188 @staticmethod 

189 def urls(): 

190 return [ 

191 "https://www.idiap.ch/software/bob/databases/latest/gbu-xmls.tar.gz", 

192 "http://www.idiap.ch/software/bob/databases/latest/gbu-xmls.tar.gz", 

193 ] 

194 

195 def background_model_samples(self): 

196 if self.background_samples is None: 

197 if self.annotations is None: 

198 self.annotations = load_annotations( 

199 search_and_open( 

200 search_pattern="alleyes.csv", base_dir=self.filename 

201 ) 

202 ) 

203 # for 

204 self.background_samples = [] 

205 

206 for b_files in self._background_files: 

207 f = search_and_open( 

208 search_pattern=f"{b_files}", base_dir=self.filename 

209 ) 

210 

211 self.background_samples += self._make_sampleset_from_filedict( 

212 read_list(f) 

213 ) 

214 return self.background_samples 

215 

216 def probes(self, group="dev"): 

217 if self.protocol not in self.probes_dict: 

218 if self.annotations is None: 

219 self.annotations = load_annotations( 

220 search_and_open( 

221 search_pattern="alleyes.csv", base_dir=self.filename 

222 ) 

223 ) 

224 

225 f = search_and_open( 

226 search_pattern=f"GBU_{self.protocol}_Query.xml", 

227 base_dir=self.filename, 

228 ) 

229 template_ids = [x.template_id for x in self.references()] 

230 

231 self.probes_dict[ 

232 self.protocol 

233 ] = self._make_sampleset_from_filedict(read_list(f), template_ids) 

234 return self.probes_dict[self.protocol] 

235 

236 def references(self, group="dev"): 

237 if self.protocol not in self.references_dict: 

238 if self.annotations is None: 

239 self.annotations = load_annotations( 

240 search_and_open( 

241 search_pattern="alleyes.csv", base_dir=self.filename 

242 ) 

243 ) 

244 

245 f = search_and_open( 

246 search_pattern=f"GBU_{self.protocol}_Target.xml", 

247 base_dir=self.filename, 

248 ) 

249 self.references_dict[ 

250 self.protocol 

251 ] = self._make_sampleset_from_filedict( 

252 read_list(f), 

253 ) 

254 

255 return self.references_dict[self.protocol] 

256 

257 def groups(self): 

258 return ["dev"] 

259 

260 def all_samples(self, group="dev"): 

261 self._check_group(group) 

262 

263 return self.references() + self.probes() 

264 

265 def _check_protocol(self, protocol): 

266 assert ( 

267 protocol in self.protocols() 

268 ), "Invalid protocol `{}` not in {}".format(protocol, self.protocols()) 

269 

270 def _check_group(self, group): 

271 assert group in self.groups(), "Invalid group `{}` not in {}".format( 

272 group, self.groups() 

273 ) 

274 

275 def _make_sampleset_from_filedict(self, file_dict, template_ids=None): 

276 samplesets = [] 

277 for key in file_dict: 

278 f = file_dict[key] 

279 

280 annotations_key = os.path.basename(f.path) 

281 

282 kwargs = ( 

283 {"references": template_ids} if template_ids is not None else {} 

284 ) 

285 

286 samplesets.append( 

287 SampleSet( 

288 key=f.path, 

289 template_id=f.template_id, 

290 subject_id=f.subject_id, 

291 **kwargs, 

292 samples=[ 

293 DelayedSample( 

294 key=f.path, 

295 annotations=self.annotations[annotations_key], 

296 load=partial( 

297 bob.io.base.load, 

298 os.path.join( 

299 self.original_directory, 

300 f.path + self.extension, 

301 ), 

302 ), 

303 ) 

304 ], 

305 ) 

306 ) 

307 return samplesets