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
« 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
6import os
7import xml.sax
9from functools import partial
11from clapper.rc import UserDefaults
13import bob.io.base
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
19rc = UserDefaults("bobrc.toml")
21"""
22GBU Database
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"""
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
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
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()
58 def startDocument(self):
59 pass
61 def endDocument(self):
62 pass
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
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 )
88 self.m_presentation = self.m_signature = self.m_path = None
89 else:
90 pass
93def subject_id_from_signature(signature):
94 return int(signature[4:])
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
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.
111 .. warning::
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
116 .. code-block:: sh
118 bob config set bob.bio.face.gbu.directory [GBU PATH]
121 The code below allows you to fetch the gallery and probes of the "Good" protocol.
123 .. code-block:: python
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()
134 """
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
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 )
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 )
160 self.references_dict = {}
161 self.probes_dict = {}
163 self.annotations = None
164 self.original_directory = original_directory
165 self.extension = extension
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 ]
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 )
184 @staticmethod
185 def protocols():
186 return ["Good", "Bad", "Ugly"]
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 ]
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 = []
206 for b_files in self._background_files:
207 f = search_and_open(
208 search_pattern=f"{b_files}", base_dir=self.filename
209 )
211 self.background_samples += self._make_sampleset_from_filedict(
212 read_list(f)
213 )
214 return self.background_samples
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 )
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()]
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]
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 )
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 )
255 return self.references_dict[self.protocol]
257 def groups(self):
258 return ["dev"]
260 def all_samples(self, group="dev"):
261 self._check_group(group)
263 return self.references() + self.probes()
265 def _check_protocol(self, protocol):
266 assert (
267 protocol in self.protocols()
268 ), "Invalid protocol `{}` not in {}".format(protocol, self.protocols())
270 def _check_group(self, group):
271 assert group in self.groups(), "Invalid group `{}` not in {}".format(
272 group, self.groups()
273 )
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]
280 annotations_key = os.path.basename(f.path)
282 kwargs = (
283 {"references": template_ids} if template_ids is not None else {}
284 )
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