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

1#!/usr/bin/env python 

2# vim: set fileencoding=utf-8 : 

3 

4import collections 

5import json 

6import logging 

7import os 

8 

9from bob.bio.base.database.utils import search_and_open 

10 

11logger = logging.getLogger(__name__) 

12 

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} 

37 

38 

39def read_annotation_file(file_name, annotation_type): 

40 """This function provides default functionality to read annotation files. 

41 

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: 

51 

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. 

61 

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. 

67 

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 

77 

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) 

85 

86 annotations = {} 

87 

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])) 

96 

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 ) 

115 

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 ) 

146 

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() 

158 

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 ) 

168 

169 return annotations