def test_label_mutability(): dummy_video = Video(backend=MediaVideo) dummy_skeleton = Skeleton() dummy_instance = Instance(dummy_skeleton) dummy_frame = LabeledFrame(dummy_video, frame_idx=0, instances=[dummy_instance]) labels = Labels() labels.append(dummy_frame) assert dummy_video in labels.videos assert dummy_video in labels assert dummy_skeleton in labels.skeletons assert dummy_skeleton in labels assert dummy_frame in labels.labeled_frames assert dummy_frame in labels assert (dummy_video, 0) in labels assert (dummy_video, 1) not in labels dummy_video2 = Video(backend=MediaVideo) dummy_skeleton2 = Skeleton(name="dummy2") dummy_instance2 = Instance(dummy_skeleton2) dummy_frame2 = LabeledFrame(dummy_video2, frame_idx=0, instances=[dummy_instance2]) assert dummy_video2 not in labels assert dummy_skeleton2 not in labels assert dummy_frame2 not in labels labels.append(dummy_frame2) assert dummy_video2 in labels assert dummy_frame2 in labels labels.remove_video(dummy_video2) assert dummy_video2 not in labels assert dummy_frame2 not in labels assert len(labels.find(dummy_video2)) == 0 assert len(labels) == 1 labels.append(LabeledFrame(dummy_video, frame_idx=0)) assert len(labels) == 1 dummy_frames = [LabeledFrame(dummy_video, frame_idx=i) for i in range(10)] dummy_frames2 = [LabeledFrame(dummy_video2, frame_idx=i) for i in range(10)] for f in dummy_frames + dummy_frames2: labels.append(f) assert len(labels) == 20 labels.remove_video(dummy_video2) assert len(labels) == 10 assert len(labels.find(dummy_video)) == 10 assert dummy_frame in labels assert all([label in labels for label in dummy_frames[1:]]) assert dummy_video2 not in labels assert len(labels.find(dummy_video2)) == 0 assert all([label not in labels for label in dummy_frames2]) labels.remove_video(dummy_video) assert len(labels.find(dummy_video)) == 0
def simple_predictions(): video = Video.from_filename("video.mp4") skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") track_a = Track(0, "a") track_b = Track(0, "b") labels = Labels() instances = [] instances.append( PredictedInstance( skeleton=skeleton, score=2, track=track_a, points=dict(a=PredictedPoint(1, 1, score=0.5), b=PredictedPoint(1, 1, score=0.5)), )) instances.append( PredictedInstance( skeleton=skeleton, score=5, track=track_b, points=dict(a=PredictedPoint(1, 1, score=0.7), b=PredictedPoint(1, 1, score=0.7)), )) labeled_frame = LabeledFrame(video, frame_idx=0, instances=instances) labels.append(labeled_frame) instances = [] instances.append( PredictedInstance( skeleton=skeleton, score=3, track=track_a, points=dict(a=PredictedPoint(4, 5, score=1.5), b=PredictedPoint(1, 1, score=1.0)), )) instances.append( PredictedInstance( skeleton=skeleton, score=6, track=track_b, points=dict(a=PredictedPoint(6, 13, score=1.7), b=PredictedPoint(1, 1, score=1.0)), )) labeled_frame = LabeledFrame(video, frame_idx=1, instances=instances) labels.append(labeled_frame) return labels
def test_duplicate_skeletons_serializing(): vid = Video.from_filename("foo.mp4") skeleton_a = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") skeleton_b = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") lf_a = LabeledFrame(vid, frame_idx=2, instances=[Instance(skeleton_a)]) lf_b = LabeledFrame(vid, frame_idx=3, instances=[Instance(skeleton_b)]) new_labels = Labels(labeled_frames=[lf_a, lf_b]) new_labels_json = new_labels.to_dict()
def _points_dict_to_array( points: Dict[Union[str, Node], Point], parray: PointArray, skeleton: Skeleton ): """ Sets values in given :class:`PointsArray` from dictionary. Args: points: The dictionary of points. Keys can be either node names or :class:`Node`s, values are :class:`Point`s. parray: The :class:`PointsArray` which is being updated. skeleton: The :class:`Skeleton` which contains the nodes referenced in the dictionary of points. Raises: ValueError: If dictionary keys are not either all strings or all :class:`Node`s. Returns: None """ # Check if the dict contains all strings is_string_dict = set(map(type, points)) == {str} # Check if the dict contains all Node objects is_node_dict = set(map(type, points)) == {Node} # If the user fed in a dict whose keys are strings, these are node names, # convert to node indices so we don't break references to skeleton nodes # if the node name is relabeled. if points and is_string_dict: points = {skeleton.find_node(name): point for name, point in points.items()} if not is_string_dict and not is_node_dict: raise ValueError( "points dictionary must be keyed by either strings " + "(node names) or Nodes." ) # Get rid of the points dict and replace with equivalent point array. for node, point in points.items(): # Convert PredictedPoint to Point if Instance if type(parray) == PointArray and type(point) == PredictedPoint: point = Point( x=point.x, y=point.y, visible=point.visible, complete=point.complete ) try: parray[skeleton.node_to_index(node)] = point # parray[skeleton.node_to_index(node.name)] = point except: pass
def test_scalar_properties(): # Scalar dummy_video = Video(backend=MediaVideo) dummy_skeleton = Skeleton() dummy_instance = Instance(dummy_skeleton) dummy_frame = LabeledFrame(dummy_video, frame_idx=0, instances=[dummy_instance]) labels = Labels() labels.append(dummy_frame) assert labels.video == dummy_video assert labels.skeleton == dummy_skeleton # Empty labels = Labels() with pytest.raises(ValueError): labels.video with pytest.raises(ValueError): labels.skeleton # More than one video dummy_skeleton = Skeleton() labels = Labels() labels.append( LabeledFrame(Video(backend=MediaVideo), frame_idx=0, instances=[Instance(dummy_skeleton)])) labels.append( LabeledFrame(Video(backend=MediaVideo), frame_idx=0, instances=[Instance(dummy_skeleton)])) assert labels.skeleton == dummy_skeleton with pytest.raises(ValueError): labels.video # More than one skeleton dummy_video = Video(backend=MediaVideo) labels = Labels() labels.append( LabeledFrame(dummy_video, frame_idx=0, instances=[Instance(Skeleton())])) labels.append( LabeledFrame(dummy_video, frame_idx=1, instances=[Instance(Skeleton())])) assert labels.video == dummy_video with pytest.raises(ValueError): labels.skeleton
def test_distinct_skeletons_serializing(): vid = Video.from_filename("foo.mp4") skeleton_a = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") skeleton_b = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") skeleton_b.add_node("foo") lf_a = LabeledFrame(vid, frame_idx=2, instances=[Instance(skeleton_a)]) lf_b = LabeledFrame(vid, frame_idx=3, instances=[Instance(skeleton_b)]) new_labels = Labels(labeled_frames=[lf_a, lf_b]) # Make sure we can serialize this new_labels_json = new_labels.to_dict()
def test_name_change(skeleton): new_skeleton = Skeleton.rename_skeleton(skeleton, "New Fly") import networkx as nx def dict_match(dict1, dict2): return dict1 == dict2 # Make sure the graphs are the same, not exact but clo assert nx.is_isomorphic(new_skeleton._graph, skeleton._graph, node_match=dict_match) # Make sure the skeletons are different (because of name) assert new_skeleton != skeleton # Make sure they hash different assert hash(new_skeleton) != hash(skeleton) # Make sure sets work assert len({new_skeleton, skeleton}) == 2 # Make sure changing the name causues problems with pytest.raises(NotImplementedError): skeleton.name = "Test"
def test_instance_access(): labels = Labels() dummy_skeleton = Skeleton() dummy_video = Video(backend=MediaVideo) dummy_video2 = Video(backend=MediaVideo) for i in range(10): labels.append( LabeledFrame( dummy_video, frame_idx=i, instances=[Instance(dummy_skeleton), Instance(dummy_skeleton)], )) for i in range(10): labels.append( LabeledFrame( dummy_video2, frame_idx=i, instances=[ Instance(dummy_skeleton), Instance(dummy_skeleton), Instance(dummy_skeleton), ], )) assert len(labels.all_instances) == 50 assert len(list(labels.instances(video=dummy_video))) == 20 assert len(list(labels.instances(video=dummy_video2))) == 30
def test_dont_unify_skeletons(): vid = Video.from_filename("foo.mp4") skeleton_a = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") skeleton_b = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") lf_a = LabeledFrame(vid, frame_idx=2, instances=[Instance(skeleton_a)]) lf_b = LabeledFrame(vid, frame_idx=3, instances=[Instance(skeleton_b)]) labels = Labels(labeled_frames=[lf_a]) labels.extend_from([lf_b], unify=False) ids = skeleton_ids_from_label_instances(labels) # Make sure we still have two distinct skeleton objects assert len(set(ids)) == 2 # Make sure we can serialize this labels.to_dict()
def test_nms_instances_to_remove(): skeleton = Skeleton() skeleton.add_nodes(("a", "b")) instances = [] inst = PredictedInstance(skeleton=skeleton) inst["a"].x = 10 inst["a"].y = 10 inst["b"].x = 20 inst["b"].y = 20 inst.score = 1 instances.append(inst) inst = PredictedInstance(skeleton=skeleton) inst["a"].x = 10 inst["a"].y = 10 inst["b"].x = 15 inst["b"].y = 15 inst.score = 0.3 instances.append(inst) inst = PredictedInstance(skeleton=skeleton) inst["a"].x = 30 inst["a"].y = 30 inst["b"].x = 40 inst["b"].y = 40 inst.score = 1 instances.append(inst) inst = PredictedInstance(skeleton=skeleton) inst["a"].x = 32 inst["a"].y = 32 inst["b"].x = 42 inst["b"].y = 42 inst.score = 0.5 instances.append(inst) to_keep, to_remove = nms_instances(instances, iou_threshold=0.5, target_count=3) assert len(to_remove) == 1 assert to_remove[0].matches(instances[1])
def test_unify_skeletons(): vid = Video.from_filename("foo.mp4") skeleton_a = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") skeleton_b = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") lf_a = LabeledFrame(vid, frame_idx=2, instances=[Instance(skeleton_a)]) lf_b = LabeledFrame(vid, frame_idx=3, instances=[Instance(skeleton_b)]) labels = Labels() labels.extend_from([lf_a], unify=True) labels.extend_from([lf_b], unify=True) ids = skeleton_ids_from_label_instances(labels) # Make sure that skeleton_b got replaced with skeleton_a when we # added the frame with "unify" set assert len(set(ids)) == 1 # Make sure we can serialize this labels.to_dict()
def test_labels_merge(): dummy_video = Video(backend=MediaVideo) dummy_skeleton = Skeleton() dummy_skeleton.add_node("node") labels = Labels() dummy_frames = [] # Add 10 instances with different points (so they aren't "redundant") for i in range(10): instance = Instance(skeleton=dummy_skeleton, points=dict(node=Point(i, i))) dummy_frame = LabeledFrame(dummy_video, frame_idx=0, instances=[instance]) dummy_frames.append(dummy_frame) labels.labeled_frames.extend(dummy_frames) assert len(labels) == 10 assert len(labels.labeled_frames[0].instances) == 1 labels.merge_matching_frames() assert len(labels) == 1 assert len(labels.labeled_frames[0].instances) == 10
def test_json(skeleton, tmpdir): """ Test saving and loading a Skeleton object in JSON. """ JSON_TEST_FILENAME = os.path.join(tmpdir, "skeleton.json") # Save it to a JSON filename skeleton.save_json(JSON_TEST_FILENAME) # Load the JSON object back in skeleton_copy = Skeleton.load_json(JSON_TEST_FILENAME) # Make sure we get back the same skeleton we saved. assert skeleton.matches(skeleton_copy)
def test_basic_suggestions(small_robot_mp4_vid): dummy_video = small_robot_mp4_vid dummy_skeleton = Skeleton() dummy_instance = Instance(dummy_skeleton) dummy_frame = LabeledFrame(dummy_video, frame_idx=0, instances=[dummy_instance]) labels = Labels() labels.append(dummy_frame) suggestions = VideoFrameSuggestions.suggest( labels=labels, params=dict(method="sample", per_video=13) ) labels.set_suggestions(suggestions) assert len(labels.get_video_suggestions(dummy_video)) == 13
def removal_test_labels(): skeleton = Skeleton() video = Video(backend=MediaVideo) lf_user_only = LabeledFrame( video=video, frame_idx=0, instances=[Instance(skeleton=skeleton)] ) lf_pred_only = LabeledFrame( video=video, frame_idx=1, instances=[PredictedInstance(skeleton=skeleton)] ) lf_both = LabeledFrame( video=video, frame_idx=2, instances=[Instance(skeleton=skeleton), PredictedInstance(skeleton=skeleton)], ) labels = Labels([lf_user_only, lf_pred_only, lf_both]) return labels
def test_merge_predictions(): dummy_video_a = Video.from_filename("foo.mp4") dummy_video_b = Video.from_filename("foo.mp4") dummy_skeleton_a = Skeleton() dummy_skeleton_a.add_node("node") dummy_skeleton_b = Skeleton() dummy_skeleton_b.add_node("node") dummy_instances_a = [] dummy_instances_a.append( Instance(skeleton=dummy_skeleton_a, points=dict(node=Point(1, 1))) ) dummy_instances_a.append( Instance(skeleton=dummy_skeleton_a, points=dict(node=Point(2, 2))) ) labels_a = Labels() labels_a.append( LabeledFrame(dummy_video_a, frame_idx=0, instances=dummy_instances_a) ) dummy_instances_b = [] dummy_instances_b.append( Instance(skeleton=dummy_skeleton_b, points=dict(node=Point(1, 1))) ) dummy_instances_b.append( PredictedInstance( skeleton=dummy_skeleton_b, points=dict(node=Point(3, 3)), score=1 ) ) labels_b = Labels() labels_b.append( LabeledFrame(dummy_video_b, frame_idx=0, instances=dummy_instances_b) ) # Frames have one redundant instance (perfect match) and all the # non-matching instances are different types (one predicted, one not). merged, extra_a, extra_b = Labels.complex_merge_between(labels_a, labels_b) assert len(merged[dummy_video_a]) == 1 assert len(merged[dummy_video_a][0]) == 1 # the predicted instance was merged assert not extra_a assert not extra_b
def test_multivideo_tracks(): vid_a = Video.from_filename("foo.mp4") vid_b = Video.from_filename("bar.mp4") skeleton = Skeleton.load_json("tests/data/skeleton/fly_skeleton_legs.json") track_a = Track(spawned_on=2, name="A") track_b = Track(spawned_on=3, name="B") inst_a = Instance(track=track_a, skeleton=skeleton) inst_b = Instance(track=track_b, skeleton=skeleton) lf_a = LabeledFrame(vid_a, frame_idx=2, instances=[inst_a]) lf_b = LabeledFrame(vid_b, frame_idx=3, instances=[inst_b]) labels = Labels(labeled_frames=[lf_a, lf_b]) # Try setting video B instance to track used in video A labels.track_swap(vid_b, new_track=track_a, old_track=track_b, frame_range=(3, 4)) assert inst_b.track == track_a
def test_deserialize_suggestions(small_robot_mp4_vid, tmpdir): dummy_video = small_robot_mp4_vid dummy_skeleton = Skeleton() dummy_instance = Instance(dummy_skeleton) dummy_frame = LabeledFrame(dummy_video, frame_idx=0, instances=[dummy_instance]) labels = Labels() labels.append(dummy_frame) suggestions = VideoFrameSuggestions.suggest( labels=labels, params=dict(method="sample", per_video=13) ) labels.set_suggestions(suggestions) filename = os.path.join(tmpdir, "new_suggestions.h5") Labels.save_file(filename=filename, labels=labels) new_suggestion_labels = Labels.load_file(filename) assert len(suggestions) == len(new_suggestion_labels.suggestions) assert [frame.frame_idx for frame in suggestions] == [ frame.frame_idx for frame in new_suggestion_labels.suggestions ]
def __init__(self, labels_path: Optional[str] = None, *args, **kwargs): """Initialize the app. Args: labels_path: Path to saved :class:`Labels` dataset. Returns: None. """ super(MainWindow, self).__init__(*args, **kwargs) self.state = GuiState() self.labels = Labels() self.commands = CommandContext(state=self.state, app=self, update_callback=self.on_data_update) self._menu_actions = dict() self._buttons = dict() self._child_windows = dict() self.overlays = dict() self.state.connect("filename", self.setWindowTitle) self.state["skeleton"] = Skeleton() self.state["labeled_frame"] = None self.state["filename"] = None self.state["show labels"] = True self.state["show edges"] = True self.state["edge style"] = "Line" self.state["fit"] = False self.state["color predicted"] = prefs["color predicted"] self._initialize_gui() if labels_path: self.loadProjectFile(labels_path)
def test_skeleton_node_name_change(): """ Test that and instance is not broken after a node on the skeleton has its name changed. """ s = Skeleton("Test") s.add_nodes(["a", "b", "c", "d", "e"]) s.add_edge("a", "b") instance = Instance(s) instance["a"] = Point(1, 2) instance["b"] = Point(3, 4) # Rename the node s.relabel_nodes({"a": "A"}) # Reference to the old node name should raise a KeyError with pytest.raises(KeyError): instance["a"].x = 2 # Make sure the A now references the same point on the instance assert instance["A"] == Point(1, 2) assert instance["b"] == Point(3, 4)
def stickman(): # Make a skeleton with a space in its name to test things. stickman = Skeleton("Stick man") stickman.add_nodes( ["head", "neck", "body", "right-arm", "left-arm", "right-leg", "left-leg"] ) stickman.add_edge("neck", "head") stickman.add_edge("body", "neck") stickman.add_edge("body", "right-arm") stickman.add_edge("body", "left-arm") stickman.add_edge("body", "right-leg") stickman.add_edge("body", "left-leg") stickman.add_symmetry(node1="left-arm", node2="right-arm") stickman.add_symmetry(node1="left-leg", node2="right-leg") return stickman
def test_complex_merge(): dummy_video_a = Video.from_filename("foo.mp4") dummy_video_b = Video.from_filename("foo.mp4") dummy_skeleton_a = Skeleton() dummy_skeleton_a.add_node("node") dummy_skeleton_b = Skeleton() dummy_skeleton_b.add_node("node") dummy_instances_a = [] dummy_instances_a.append( Instance(skeleton=dummy_skeleton_a, points=dict(node=Point(1, 1)))) dummy_instances_a.append( Instance(skeleton=dummy_skeleton_a, points=dict(node=Point(2, 2)))) labels_a = Labels() labels_a.append( LabeledFrame(dummy_video_a, frame_idx=0, instances=dummy_instances_a)) dummy_instances_b = [] dummy_instances_b.append( Instance(skeleton=dummy_skeleton_b, points=dict(node=Point(1, 1)))) dummy_instances_b.append( Instance(skeleton=dummy_skeleton_b, points=dict(node=Point(3, 3)))) labels_b = Labels() labels_b.append( LabeledFrame(dummy_video_b, frame_idx=0, instances=dummy_instances_b)) # conflict labels_b.append( LabeledFrame(dummy_video_b, frame_idx=1, instances=dummy_instances_b)) # clean merged, extra_a, extra_b = Labels.complex_merge_between(labels_a, labels_b) # Check that we have the cleanly merged frame assert dummy_video_a in merged assert len(merged[dummy_video_a]) == 1 # one merged frame assert len(merged[dummy_video_a][1]) == 2 # with two instances # Check that labels_a includes redundant and clean assert len(labels_a.labeled_frames) == 2 assert len(labels_a.labeled_frames[0].instances) == 1 assert labels_a.labeled_frames[0].instances[0].points[0].x == 1 assert len(labels_a.labeled_frames[1].instances) == 2 assert labels_a.labeled_frames[1].instances[0].points[0].x == 1 assert labels_a.labeled_frames[1].instances[1].points[0].x == 3 # Check that extra_a/b includes the appropriate conflicting instance assert len(extra_a) == 1 assert len(extra_b) == 1 assert len(extra_a[0].instances) == 1 assert len(extra_b[0].instances) == 1 assert extra_a[0].instances[0].points[0].x == 2 assert extra_b[0].instances[0].points[0].x == 3 # Check that objects were unified assert extra_a[0].video == extra_b[0].video # Check resolving the conflict using new Labels.finish_complex_merge(labels_a, extra_b) assert len(labels_a.labeled_frames) == 2 assert len(labels_a.labeled_frames[0].instances) == 2 assert labels_a.labeled_frames[0].instances[1].points[0].x == 3
def test_symmetry(): s1 = Skeleton("s1") s1.add_nodes(["1", "2", "3", "4", "5", "6"]) s1.add_edge("1", "2") s1.add_edge("3", "4") s1.add_edge("5", "6") s1.add_symmetry("1", "5") s1.add_symmetry("3", "6") assert (s1.nodes[0], s1.nodes[4]) in s1.symmetries assert (s1.nodes[2], s1.nodes[5]) in s1.symmetries assert len(s1.symmetries) == 2 assert (0, 4) in s1.symmetric_inds assert (2, 5) in s1.symmetric_inds assert len(s1.symmetric_inds) == 2 assert s1.get_symmetry("1").name == "5" assert s1.get_symmetry("5").name == "1" assert s1.get_symmetry("3").name == "6" # Cannot add more than one symmetry to a node with pytest.raises(ValueError): s1.add_symmetry("1", "6") with pytest.raises(ValueError): s1.add_symmetry("6", "1") s1.delete_symmetry("1", "5") assert s1.get_symmetry("1") is None with pytest.raises(ValueError): s1.delete_symmetry("1", "5")
def test_arborescence(): skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") skeleton.add_node("c") # linear: a -> b -> c skeleton.add_edge("a", "b") skeleton.add_edge("b", "c") assert skeleton.is_arborescence skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") skeleton.add_node("c") # two branches from a: a -> b and a -> c skeleton.add_edge("a", "b") skeleton.add_edge("a", "c") assert skeleton.is_arborescence skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") skeleton.add_node("c") # no edges so too many roots assert not skeleton.is_arborescence assert sorted((n.name for n in skeleton.root_nodes)) == ["a", "b", "c"] # still too many roots: a and c skeleton.add_edge("a", "b") assert not skeleton.is_arborescence assert sorted((n.name for n in skeleton.root_nodes)) == ["a", "c"] skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") skeleton.add_node("c") # cycle skeleton.add_edge("a", "b") skeleton.add_edge("b", "c") skeleton.add_edge("c", "a") assert not skeleton.is_arborescence assert len(skeleton.cycles) == 1 assert len(skeleton.root_nodes) == 0 skeleton = Skeleton() skeleton.add_node("a") skeleton.add_node("b") skeleton.add_node("c") skeleton.add_node("d") # diamond, too many sources leading to d skeleton.add_edge("a", "b") skeleton.add_edge("a", "c") skeleton.add_edge("b", "d") skeleton.add_edge("c", "d") assert not skeleton.is_arborescence assert len(skeleton.cycles) == 0 assert len(skeleton.root_nodes) == 1 assert len(skeleton.in_degree_over_one) == 1
def from_json_data(cls, data: Union[str, dict], match_to: Optional["Labels"] = None) -> "Labels": """ Create instance of class from data in dictionary. Method is used by other methods that load from JSON. Args: data: Dictionary, deserialized from JSON. match_to: If given, we'll replace particular objects in the data dictionary with *matching* objects in the match_to :class:`Labels` object. This ensures that the newly instantiated :class:`Labels` can be merged without duplicate matching objects (e.g., :class:`Video` objects ). Returns: A new :class:`Labels` object. """ # Parse the json string if needed. if type(data) is str: dicts = json_loads(data) else: dicts = data dicts["tracks"] = dicts.get( "tracks", []) # don't break if json doesn't include tracks # First, deserialize the skeletons, videos, and nodes lists. # The labels reference these so we will need them while deserializing. nodes = cattr.structure(dicts["nodes"], List[Node]) idx_to_node = {i: nodes[i] for i in range(len(nodes))} skeletons = Skeleton.make_cattr(idx_to_node).structure( dicts["skeletons"], List[Skeleton]) videos = Video.cattr().structure(dicts["videos"], List[Video]) try: # First try unstructuring tuple (newer format) track_cattr = cattr.Converter( unstruct_strat=cattr.UnstructureStrategy.AS_TUPLE) tracks = track_cattr.structure(dicts["tracks"], List[Track]) except: # Then try unstructuring dict (older format) try: tracks = cattr.structure(dicts["tracks"], List[Track]) except: raise ValueError("Unable to load tracks as tuple or dict!") # if we're given a Labels object to match, use its objects when they match if match_to is not None: for idx, sk in enumerate(skeletons): for old_sk in match_to.skeletons: if sk.matches(old_sk): # use nodes from matched skeleton for (node, match_node) in zip(sk.nodes, old_sk.nodes): node_idx = nodes.index(node) nodes[node_idx] = match_node # use skeleton from match skeletons[idx] = old_sk break for idx, vid in enumerate(videos): for old_vid in match_to.videos: # Try to match videos using either their current or source filename # if available. old_vid_paths = [old_vid.filename] if getattr(old_vid.backend, "has_embedded_images", False): old_vid_paths.append( old_vid.backend._source_video.filename) new_vid_paths = [vid.filename] if getattr(vid.backend, "has_embedded_images", False): new_vid_paths.append( vid.backend._source_video.filename) is_match = False for old_vid_path in old_vid_paths: for new_vid_path in new_vid_paths: if old_vid_path == new_vid_path or weak_filename_match( old_vid_path, new_vid_path): is_match = True videos[idx] = old_vid break if is_match: break if is_match: break suggestions = [] if "suggestions" in dicts: suggestions_cattr = cattr.Converter() suggestions_cattr.register_structure_hook( Video, lambda x, type: videos[int(x)]) try: suggestions = suggestions_cattr.structure( dicts["suggestions"], List[SuggestionFrame]) except Exception as e: print("Error while loading suggestions (1)") print(e) try: # Convert old suggestion format to new format. # Old format: {video: list of frame indices} # New format: [SuggestionFrames] old_suggestions = suggestions_cattr.structure( dicts["suggestions"], Dict[Video, List]) for video in old_suggestions.keys(): suggestions.extend([ SuggestionFrame(video, idx) for idx in old_suggestions[video] ]) except Exception as e: print("Error while loading suggestions (2)") print(e) pass if "negative_anchors" in dicts: negative_anchors_cattr = cattr.Converter() negative_anchors_cattr.register_structure_hook( Video, lambda x, type: videos[int(x)]) negative_anchors = negative_anchors_cattr.structure( dicts["negative_anchors"], Dict[Video, List]) else: negative_anchors = dict() if "provenance" in dicts: provenance = dicts["provenance"] else: provenance = dict() # If there is actual labels data, get it. if "labels" in dicts: label_cattr = make_instance_cattr() label_cattr.register_structure_hook( Skeleton, lambda x, type: skeletons[int(x)]) label_cattr.register_structure_hook(Video, lambda x, type: videos[int(x)]) label_cattr.register_structure_hook( Node, lambda x, type: x if isinstance(x, Node) else nodes[int(x)]) label_cattr.register_structure_hook( Track, lambda x, type: None if x is None else tracks[int(x)]) labels = label_cattr.structure(dicts["labels"], List[LabeledFrame]) else: labels = [] return Labels( labeled_frames=labels, videos=videos, skeletons=skeletons, nodes=nodes, suggestions=suggestions, negative_anchors=negative_anchors, tracks=tracks, provenance=provenance, )
def test_symmetry(): s1 = Skeleton("s1") s1.add_nodes(["1", "2", "3", "4", "5", "6"]) s1.add_edge("1", "2") s1.add_edge("3", "4") s1.add_edge("5", "6") s1.add_symmetry("1", "5") s1.add_symmetry("3", "6") assert s1.get_symmetry("1").name == "5" assert s1.get_symmetry("5").name == "1" assert s1.get_symmetry("3").name == "6" # Cannot add more than one symmetry to a node with pytest.raises(ValueError): s1.add_symmetry("1", "6") with pytest.raises(ValueError): s1.add_symmetry("6", "1") s1.delete_symmetry("1", "5") assert s1.get_symmetry("1") is None with pytest.raises(ValueError): s1.delete_symmetry("1", "5")
def test_eq(): s1 = Skeleton("s1") s1.add_nodes(["1", "2", "3", "4", "5", "6"]) s1.add_edge("1", "2") s1.add_edge("3", "4") s1.add_edge("5", "6") s1.add_symmetry("3", "6") # Make a copy check that they are equal s2 = copy.deepcopy(s1) assert s1.matches(s2) # Add an edge, check that they are not equal s2 = copy.deepcopy(s1) s2.add_edge("5", "1") assert not s1.matches(s2) # Add a symmetry edge, not equal s2 = copy.deepcopy(s1) s2.add_symmetry("5", "1") assert not s1.matches(s2) # Delete a node s2 = copy.deepcopy(s1) s2.delete_node("5") assert not s1.matches(s2) # Delete and edge, not equal s2 = copy.deepcopy(s1) s2.delete_edge("1", "2") assert not s1.matches(s2)
def test_hdf5(skeleton, stickman, tmpdir): filename = os.path.join(tmpdir, "skeleton.h5") if os.path.isfile(filename): os.remove(filename) # Save both skeletons to the HDF5 filename skeleton.save_hdf5(filename) stickman.save_hdf5(filename) # Load the all the skeletons as a list sk_list = Skeleton.load_all_hdf5(filename) # Lets check that they are equal to what we saved, this checks the order too. assert skeleton.matches(sk_list[0]) assert stickman.matches(sk_list[1]) # Check load to dict as well sk_dict = Skeleton.load_all_hdf5(filename, return_dict=True) assert skeleton.matches(sk_dict[skeleton.name]) assert stickman.matches(sk_dict[stickman.name]) # Check individual load assert Skeleton.load_hdf5(filename, skeleton.name).matches(skeleton) assert Skeleton.load_hdf5(filename, stickman.name).matches(stickman) # Check overwrite save and save list Skeleton.save_all_hdf5(filename, [skeleton, stickman]) assert Skeleton.load_hdf5(filename, skeleton.name).matches(skeleton) assert Skeleton.load_hdf5(filename, stickman.name).matches(stickman) # Make sure we can't load a non-existent skeleton with pytest.raises(KeyError): Skeleton.load_hdf5(filename, "BadName") # Make sure we can't save skeletons with the same name with pytest.raises(ValueError): Skeleton.save_all_hdf5( filename, [skeleton, Skeleton(name=skeleton.name)])
def test_load_mat_format(): skeleton = Skeleton.load_mat( "tests/data/skeleton/leap_mat_format/skeleton_legs.mat") # Check some stuff about the skeleton we loaded assert len(skeleton.nodes) == 24 assert len(skeleton.edges) == 23 # The node and edge list that should be present in skeleton_legs.mat node_names = [ "head", "neck", "thorax", "abdomen", "wingL", "wingR", "forelegL1", "forelegL2", "forelegL3", "forelegR1", "forelegR2", "forelegR3", "midlegL1", "midlegL2", "midlegL3", "midlegR1", "midlegR2", "midlegR3", "hindlegL1", "hindlegL2", "hindlegL3", "hindlegR1", "hindlegR2", "hindlegR3", ] edges = [ [2, 1], [1, 0], [2, 3], [2, 4], [2, 5], [2, 6], [6, 7], [7, 8], [2, 9], [9, 10], [10, 11], [2, 12], [12, 13], [13, 14], [2, 15], [15, 16], [16, 17], [2, 18], [18, 19], [19, 20], [2, 21], [21, 22], [22, 23], ] assert [n.name for n in skeleton.nodes] == node_names # Check the edges and their order for i, edge in enumerate(skeleton.edge_names): assert tuple(edges[i]) == ( skeleton.node_to_index(edge[0]), skeleton.node_to_index(edge[1]), )
def test_edge_order(): """Test is edge list order is maintained upon insertion""" skeleton = Skeleton("Test")