Skip to content

Commit 4584271

Browse files
committed
Merge branch 'release-202311-Eval2-v1.0.1'
* release-202311-Eval2-v1.0.1: Renamed eval logger to be specific to MIT-LL Added some TODO's to use config structures instead of raw loads Use common constant vars instead of floating strings Spread the config love Add Eval2 logging for emitted error notifications Style fixups Add logging node for Eval2 requested format Added node to translate TaskUpdate messages to broad level Add activity label config structs Add common task config structures and load functions Add another time conversion function Hannah requested enabling of all task configs in multi config Update log message in GSP node to indicate which task it pertained
2 parents 10d0f5d + e823f4e commit 4584271

File tree

12 files changed

+726
-58
lines changed

12 files changed

+726
-58
lines changed

angel_system/data/config_structs.py

+160-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
Structures related to configuration files.
33
"""
44

5-
from dataclasses import dataclass, field
5+
from dataclasses import dataclass
66
from os import PathLike
7+
from pathlib import Path
78
from typing import cast
89
from typing import Dict
10+
from typing import Optional
911
from typing import Sequence
1012
from typing import Tuple
1113

@@ -34,7 +36,7 @@ class ObjectLabelSet:
3436

3537
def __post_init__(self):
3638
# coerce nested label objects into the ObjectLabel type.
37-
if self.labels and not isinstance(self.labels, ObjectLabel):
39+
if self.labels and not isinstance(self.labels[0], ObjectLabel):
3840
raw_labels = cast(Sequence[Dict], self.labels)
3941
self.labels = tuple(ObjectLabel(**rl) for rl in raw_labels)
4042

@@ -50,3 +52,159 @@ def load_object_label_set(filepath: PathLike) -> ObjectLabelSet:
5052
with open(filepath) as infile:
5153
data = yaml.safe_load(infile)
5254
return ObjectLabelSet(**data)
55+
56+
57+
@dataclass
58+
class ActivityLabel:
59+
"""
60+
One activity classification ID and paired label information.
61+
"""
62+
63+
# Identifier integer for this activity label
64+
id: int
65+
# Concise string label for this activity. Should not contain any spaces.
66+
label: str
67+
# Full sentence description of this activity.
68+
full_str: str
69+
# Optional integer representing how many times an activity should be
70+
# repeated to be considered "full"
71+
# TODO: This parameter has ambiguous and violated meaning (not used as
72+
# intended if at all).
73+
repeat: Optional[int] = None
74+
75+
76+
@dataclass
77+
class ActivityLabelSet:
78+
version: str
79+
title: str
80+
labels: Tuple[ActivityLabel]
81+
82+
def __post_init__(self):
83+
# coerce nested label objects into the ObjectLabel type.
84+
if self.labels and not isinstance(self.labels[0], ActivityLabel):
85+
raw_labels = cast(Sequence[Dict], self.labels)
86+
self.labels = tuple(ActivityLabel(**rl) for rl in raw_labels)
87+
88+
89+
def load_activity_label_set(filepath: PathLike) -> ActivityLabelSet:
90+
"""
91+
Load from YAML file an activity label set configuration.
92+
93+
:param filepath: Filepath to load from.
94+
95+
:return: Structure containing the loaded configuration.
96+
"""
97+
with open(filepath) as infile:
98+
data = yaml.safe_load(infile)
99+
return ActivityLabelSet(**data)
100+
101+
102+
@dataclass
103+
class TaskStep:
104+
"""
105+
A single task step with activity components.
106+
"""
107+
108+
id: int
109+
label: str
110+
full_str: str
111+
activity_ids: Tuple[int]
112+
113+
114+
@dataclass
115+
class LinearTask:
116+
"""
117+
A linear task with steps composed of activities.
118+
"""
119+
120+
version: str
121+
title: str
122+
labels: Tuple[TaskStep]
123+
124+
def __post_init__(self):
125+
# Coerce pathlike input (str) into a Path instance if not already.
126+
if self.labels and not isinstance(self.labels[0], TaskStep):
127+
raw = cast(Sequence[Dict], self.labels)
128+
self.labels = tuple(TaskStep(**r) for r in raw)
129+
130+
131+
def load_linear_task_config(filepath: PathLike) -> LinearTask:
132+
"""
133+
Load from YAML file a linear task configuration.
134+
135+
:param filepath: Filepath to load from.
136+
137+
:return: Structure containing the loaded configuration.
138+
"""
139+
with open(filepath) as infile:
140+
data = yaml.safe_load(infile)
141+
return LinearTask(**data)
142+
143+
144+
@dataclass
145+
class OneTaskConfig:
146+
"""
147+
Specification of where one task configuration is located.
148+
"""
149+
150+
id: int
151+
label: str
152+
config_file: Path
153+
active: bool
154+
155+
def __post_init__(self):
156+
# Coerce pathlike input (str) into a Path instance if not already.
157+
# Interpret relative paths now to absolute based on current working
158+
# directory.
159+
if not isinstance(self.config_file, Path):
160+
self.config_file = Path(self.config_file).absolute()
161+
162+
163+
@dataclass
164+
class MultiTaskConfig:
165+
"""
166+
A collection of linear task configurations.
167+
"""
168+
169+
version: str
170+
title: str
171+
tasks: Tuple[OneTaskConfig]
172+
173+
def __post_init__(self):
174+
# coerce nested task objects into OneTaskConfig types
175+
if self.tasks and not isinstance(self.tasks[0], OneTaskConfig):
176+
raw = cast(Sequence[Dict], self.tasks)
177+
self.tasks = tuple(OneTaskConfig(**r) for r in raw)
178+
179+
180+
def load_multi_task_config(filepath: PathLike):
181+
"""
182+
Relative file paths are currently interpreted relative to the current
183+
working directory and resolved to be absolute.
184+
185+
:param filepath: Filepath to load from.
186+
187+
:return: Structure containing the loaded configuration.
188+
"""
189+
with open(filepath) as infile:
190+
data = yaml.safe_load(infile)
191+
return MultiTaskConfig(**data)
192+
193+
194+
def load_active_task_configs(cfg: MultiTaskConfig) -> Dict[str, LinearTask]:
195+
"""
196+
Load task configurations that are enabled in the multitask configuration.
197+
198+
:param cfg: Multitask configuration to base loading on.
199+
200+
:raises FileNotFoundError: Configured task configuration file did not refer
201+
to an open-able file.
202+
203+
:return: Mapping of task label from the input configuration to the
204+
LinearTask instance loaded.
205+
"""
206+
return {
207+
ct.label: load_linear_task_config(ct.config_file)
208+
for ct in cfg.tasks
209+
if ct.active
210+
}

angel_system/global_step_prediction/global_step_predictor.py

+10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ def __init__(
2828
GlobalStepPredctor: based on a TCN activity classifier's activity classification
2929
outputs + a set of recipes, track what step a user is on for multiple recipes.
3030
"""
31+
# TODO: make use of angel_system.data.config_structs instead of
32+
# manually loading and accessing by string keys.
3133
with open(activity_config_fpath, "r") as stream:
3234
self.activity_config = yaml.safe_load(stream)
3335
num_activity_classes = len(self.activity_config["labels"])
@@ -68,6 +70,8 @@ def __init__(
6870
self.activity_conf_history = np.empty((0, num_activity_classes))
6971

7072
self.recipe_types = recipe_types
73+
# TODO: Expect use of angel_system.data.config_structs instead of
74+
# a raw dictionary.
7175
self.recipe_configs = recipe_config_dict
7276

7377
# Array of tracker dicts
@@ -116,6 +120,8 @@ def get_activity_order_from_config(self, config_fn):
116120
Get the order of activity_ids (mapping to granular step
117121
number) based on a recipe config
118122
"""
123+
# TODO: make use of angel_system.data.config_structs instead of
124+
# manually loading and accessing by string keys.
119125
with open(config_fn, "r") as stream:
120126
config = yaml.safe_load(stream)
121127
broad_steps = config["labels"]
@@ -208,6 +214,8 @@ def initialize_new_recipe_tracker(self, recipe, config_fn=None):
208214
config_fn = self.recipe_configs[recipe]
209215

210216
# Read in task config
217+
# TODO: make use of angel_system.data.config_structs instead of
218+
# manually loading and accessing by string keys.
211219
with open(config_fn, "r") as stream:
212220
config = yaml.safe_load(stream)
213221
labels = [self.sanitize_str(l["full_str"]) for l in config["labels"]]
@@ -1025,6 +1033,8 @@ def get_gt_steps_from_gt_activities(self, video_dset, config_fn):
10251033
def sanitize_str(str_: str):
10261034
return str_.lower().strip(" .")
10271035

1036+
# TODO: make use of angel_system.data.config_structs instead of
1037+
# manually loading and accessing by string keys.
10281038
with open(config_fn, "r") as stream:
10291039
config = yaml.safe_load(stream)
10301040
labels = [sanitize_str(l["label"]) for l in config["labels"]]

config/tasks/multi-task-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ tasks:
1818
- id: 1
1919
label: "Tea"
2020
config_file: "./config/tasks/recipe_tea.yaml"
21-
active: false
21+
active: true
2222
- id: 2
2323
label: "Pinwheel"
2424
config_file: "./config/tasks/recipe_pinwheel.yaml"
25-
active: false
25+
active: true
2626
- id: 3
2727
label: "Oatmeal"
2828
config_file: "./config/tasks/recipe_oatmeal.yaml"
29-
active: false
29+
active: true
3030
- id: 4
3131
label: "Dessert Quesadilla"
3232
config_file: "./config/tasks/recipe_dessertquesadilla.yaml"

ros/angel_system_nodes/angel_system_nodes/eval/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)