# default_exp bbox_video_annotator

Bbox video annotatorΒΆ

#export

class BBoxVideoAnnotator(BBoxAnnotator):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **{**kwargs, 'render_previous_coords': False})
        pub.unsubscribe(self.controller._idx_changed, f'{self.app_state.root_topic}.index')
        pub.unsubAll(f'{self.app_state.root_topic}.index')
        state_params = {**self.bbox_state.dict()}
        state_params.pop('_uuid', [])
        state_params.pop('event_map', [])
        self.bbox_state = BBoxVideoState(
            uuid=self.bbox_state._uuid,
            **state_params
        )

        self.controller = BBoxVideoAnnotatorController(
            app_state=self.app_state,
            bbox_state=self.bbox_state,
            storage=self.storage
        )

        self.view = BBoxAnnotatorVideoGUI(
            app_state=self.app_state,
            bbox_state=self.bbox_state,
            on_save_btn_clicked=self.on_save_btn_clicked,
            drawing_enabled=self._output_item.drawing_enabled,
            on_label_changed=self.update_labels,
            on_join_btn_clicked=self.merge_tracks_selected,
            on_bbox_drawn=self.controller.sync_labels,
            fit_canvas=self._input_item.fit_canvas
        )

        self.view.on_client_ready(self.controller.handle_client_ready)

    def update_labels(self, change: dict, index: int):
        """Saves bbox_canvas_state coordinates data
        on bbox_state and save all on storage."""
        self.bbox_state.set_quietly('coords', self.view._image_box._state.bbox_coords)
        # "BBoxAnnotatorController" has no attribute "update_storage_labels"
        self.controller.update_storage_labels(change, index)  # type: ignore

    def on_save_btn_clicked(self, bbox_coords: List[BboxVideoCoordinate]):
        self.controller.save_current_annotations(bbox_coords)  # type: ignore

    def _update_state_id(self, merged_ids: List[str], bbox_coords: List[BboxVideoCoordinate]):
        merged_id = "-".join(merged_ids)

        for i, coord in enumerate(bbox_coords):
            if merged_id and bbox_coords[i].id in merged_ids:
                bbox_coords[i].id = merged_id

    def merge_tracks_selected(self, change: Dict):
        # "BBoxState" has no attribute "bbox_coords_selected"
        selecteds = self.bbox_state.bbox_coords_selected  # type: ignore

        if selecteds:
            merged_ids = [selected.bbox_video_coordinate.id for selected in selecteds]

            self._update_state_id(
                # Argument "bbox_coords" to "_update_state_id" of "BBoxVideoAnnotator" has
                #incompatible type "List[BboxCoordinate]"; expected "List[BboxVideoCoordinate]"
                merged_ids=merged_ids,  # type: ignore
                bbox_coords=self.view._image_box.state.bbox_coords  # type: ignore
            )

            # "BBoxAnnotatorController" has no attribute "update_storage_id"
            self.controller.update_storage_id(  # type: ignore
                merged_ids=merged_ids
            )

            # merge trajectories
            key = "-".join(merged_ids)
            # "BBoxState" has no attribute "trajectory"
            tmp_trajectories = deepcopy(self.bbox_state.trajectories)  # type: ignore
            for id, bbx in tmp_trajectories.items():
                if id in merged_ids:
                    try:
                        self.bbox_state.trajectories[key] += bbx  # type: ignore
                    except Exception:
                        self.bbox_state.trajectories[key] = bbx  # type: ignore

                    if id in self.bbox_state.trajectories:  # type: ignore
                        # delete old trajectory id
                        del self.bbox_state.trajectories[id]  # type: ignore

            # finished merge cleans selected bbox coords
            self.bbox_state.bbox_coords_selected = []

            self.view.clear_right_menu()
            self.view.render_right_menu(self.view._image_box.state.bbox_coords)
from ipyannotator.storage import construct_annotation_path


def delete_result_file():
    ! rm -rf ../data/projects/bbox_video/results


@pytest.fixture
def bbox_video_fixture():
    delete_result_file()
    project_path = Path('../data/projects/bbox_video')
    anno_file_path = construct_annotation_path(project_path)
    in_p = InputImage(image_dir='pics', image_width=640, image_height=400)
    out_p = OutputVideoBbox(classes=['Label 01', 'Label 02'])

    return BBoxVideoAnnotator(
        project_path=project_path,
        input_item=in_p,
        output_item=out_p,
        annotation_file_path=anno_file_path
    )


@pytest.fixture
def trajectory_fixture(bbox_video_fixture):
    fixture = bbox_video_fixture

    coords = [
        BboxVideoCoordinate(x=371, y=405, width=81, height=249, id='0'),
        BboxVideoCoordinate(x=677, y=186, width=78, height=171, id='1')
    ]

    fixture.view._image_box._state.bbox_coords = coords

    fixture.view._navi._next_btn.click()

    coords = [
        BboxVideoCoordinate(x=374, y=408, width=90, height=189, id='0'),
        BboxVideoCoordinate(x=686, y=189, width=75, height=135, id='1')
    ]

    fixture.view._image_box._state.bbox_coords = coords

    fixture.view._navi._next_btn.click()

    fixture.view._navi._prev_btn.click()
    fixture.view._navi._prev_btn.click()

    assert fixture.app_state.index == 0
    assert len(fixture.view.right_menu.children) == 2

    return fixture