Coverage for src/bob/bio/base/utils/annotations.py: 42%
52 statements
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-14 21:41 +0100
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-14 21:41 +0100
1#!/usr/bin/env python
2# vim: set fileencoding=utf-8 :
4import collections
5import json
6import logging
7import os
9from bob.bio.base.database.utils import search_and_open
11logger = logging.getLogger(__name__)
13_idiap_annotations = {
14 1: "reyeo",
15 2: "reyet",
16 3: "reyep",
17 4: "reyeb",
18 5: "reyei",
19 6: "leyei",
20 7: "leyet",
21 8: "leyep",
22 9: "leyeb",
23 10: "leyeo",
24 11: "rbrowo",
25 12: "rbrowi",
26 13: "lbrowi",
27 14: "lbrowo",
28 15: "noser",
29 16: "noset",
30 17: "nosel",
31 18: "mouthr",
32 19: "moutht",
33 20: "mouthb",
34 21: "mouthl",
35 22: "chin",
36}
39def read_annotation_file(file_name, annotation_type):
40 """This function provides default functionality to read annotation files.
42 Parameters
43 ----------
44 file_name : str
45 The full path of the annotation file to read. The path can also be like
46 ``base_path:relative_path`` where the base_path can be both a directory or
47 a tarball. This allows you to read annotations from inside a tarball.
48 annotation_type : str
49 The type of the annotation file that should be read. The following
50 annotation_types are supported:
52 * ``eyecenter``: The file contains a single row with four entries:
53 ``re_x re_y le_x le_y``
54 * ``named``: The file contains named annotations, one per line, e.g.:
55 ``reye re_x re_y`` or ``pose 25.7``
56 * ``idiap``: The file contains enumerated annotations, one per line,
57 e.g.: ``1 key1_x key1_y``, and maybe some additional annotations like
58 gender, age, ...
59 * ``json``: The file contains annotations of any format, dumped in a
60 text json file.
62 Returns
63 -------
64 dict
65 A python dictionary with the keypoint name as key and the
66 position ``(y,x)`` as value, and maybe some additional annotations.
68 Raises
69 ------
70 IOError
71 If the annotation file is not found.
72 ValueError
73 If the annotation type is not known.
74 """
75 if not file_name:
76 return None
78 if ":" in file_name:
79 base_path, tail = file_name.split(":", maxsplit=1)
80 f = search_and_open(search_pattern=tail, base_dir=base_path)
81 else:
82 if not os.path.exists(file_name):
83 raise IOError("The annotation file '%s' was not found" % file_name)
84 f = open(file_name)
86 annotations = {}
88 try:
89 if str(annotation_type) == "eyecenter":
90 # only the eye positions are written, all are in the first row
91 line = f.readline()
92 positions = line.split()
93 assert len(positions) == 4
94 annotations["reye"] = (float(positions[1]), float(positions[0]))
95 annotations["leye"] = (float(positions[3]), float(positions[2]))
97 elif str(annotation_type) == "named":
98 # multiple lines, no header line, each line contains annotation and
99 # position or single value annotation
100 for line in f:
101 positions = line.split()
102 if len(positions) == 3:
103 annotations[positions[0]] = (
104 float(positions[2]),
105 float(positions[1]),
106 )
107 elif len(positions) == 2:
108 annotations[positions[0]] = float(positions[1])
109 else:
110 logger.error(
111 "Could not interpret line '%s' in annotation file '%s'",
112 line,
113 file_name,
114 )
116 elif str(annotation_type) == "idiap":
117 # Idiap format: multiple lines, no header, each line contains an integral
118 # keypoint identifier, or other identifier like 'gender', 'age',...
119 for line in f:
120 positions = line.rstrip().split()
121 if positions:
122 if positions[0].isdigit():
123 # position field
124 assert len(positions) == 3
125 id = int(positions[0])
126 annotations[_idiap_annotations[id]] = (
127 float(positions[2]),
128 float(positions[1]),
129 )
130 else:
131 # another field, we take the first entry as key and the rest as
132 # values
133 annotations[positions[0]] = positions[1:]
134 # finally, we add the eye center coordinates as the center between the
135 # eye corners; the annotations 3 and 8 are the pupils...
136 if "reyeo" in annotations and "reyei" in annotations:
137 annotations["reye"] = (
138 (annotations["reyeo"][0] + annotations["reyei"][0]) / 2.0,
139 (annotations["reyeo"][1] + annotations["reyei"][1]) / 2.0,
140 )
141 if "leyeo" in annotations and "leyei" in annotations:
142 annotations["leye"] = (
143 (annotations["leyeo"][0] + annotations["leyei"][0]) / 2.0,
144 (annotations["leyeo"][1] + annotations["leyei"][1]) / 2.0,
145 )
147 elif str(annotation_type) == "json":
148 annotations = json.load(
149 f, object_pairs_hook=collections.OrderedDict
150 )
151 else:
152 raise ValueError(
153 "The given annotation type '%s' is not known, choose one of ('eyecenter', 'named', 'idiap')"
154 % annotation_type
155 )
156 finally:
157 f.close()
159 if (
160 annotations is not None
161 and "leye" in annotations
162 and "reye" in annotations
163 and annotations["leye"][1] < annotations["reye"][1]
164 ):
165 logger.warn(
166 "The eye annotations in file '%s' might be exchanged!" % file_name
167 )
169 return annotations