def _track_step(
        self, curr_frame: "torch.Tensor", prev_frame: "torch.Tensor", rect: "torch.Tensor"
    ) -> "torch.Tensor":
        """
        Track current frame.

        :param curr_frame: Current frame.
        :param prev_frame: Previous frame.
        :return: bounding box of previous frame
        """
        import torch

        prev_bbox = rect

        k_context_factor = 2

        def compute_output_height_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Compute height of search/target region.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: Output height.
            """
            bbox_height = bbox_tight[3] - bbox_tight[1]
            output_height = k_context_factor * bbox_height

            return torch.maximum(torch.tensor(1.0).to(self.device), output_height)

        def compute_output_width_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Compute width of search/target region.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: Output width.
            """
            bbox_width = bbox_tight[2] - bbox_tight[0]
            output_width = k_context_factor * bbox_width

            return torch.maximum(torch.tensor(1.0).to(self.device), output_width)

        def get_center_x_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Compute x-coordinate of the bounding box center.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: x-coordinate of the bounding box center.
            """
            return (bbox_tight[0] + bbox_tight[2]) / 2.0

        def get_center_y_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Compute y-coordinate of the bounding box center

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: y-coordinate of the bounding box center.
            """
            return (bbox_tight[1] + bbox_tight[3]) / 2.0

        def compute_crop_pad_image_location(
            bbox_tight: "torch.Tensor", image: "torch.Tensor"
        ) -> tuple["torch.Tensor", "torch.Tensor", "torch.Tensor", "torch.Tensor"]:
            """
            Get the valid image coordinates for the context region in target or search region in full image

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :param image: Frame to be cropped and padded.
            :return: x-coordinate of the bounding box center.
            """

            # Center of the bounding box
            # bbox_center_x = bbox_tight.get_center_x()
            # bbox_center_y = bbox_tight.get_center_y()
            bbox_center_x = get_center_x_f(bbox_tight)
            bbox_center_y = get_center_y_f(bbox_tight)

            image_height = image.shape[0]
            image_width = image.shape[1]

            # Padded output width and height
            # output_width = bbox_tight.compute_output_width()
            # output_height = bbox_tight.compute_output_height()
            output_width = compute_output_width_f(bbox_tight)
            output_height = compute_output_height_f(bbox_tight)

            roi_left = torch.maximum(torch.tensor(0.0).to(self.device), bbox_center_x - (output_width / 2.0))
            roi_bottom = torch.maximum(torch.tensor(0.0).to(self.device), bbox_center_y - (output_height / 2.0))

            # New ROI width
            # -------------
            # 1. left_half should not go out of bound on the left side of the
            # image
            # 2. right_half should not go out of bound on the right side of the
            # image
            left_half = torch.minimum(output_width / 2.0, bbox_center_x)
            right_half = torch.minimum(output_width / 2.0, image_width - bbox_center_x)
            roi_width = torch.maximum(torch.tensor(1.0).to(self.device), left_half + right_half)

            # New ROI height
            # Similar logic applied that is applied for 'New ROI width'
            top_half = torch.minimum(output_height / 2.0, bbox_center_y)
            bottom_half = torch.minimum(output_height / 2.0, image_height - bbox_center_y)
            roi_height = torch.maximum(torch.tensor(1.0).to(self.device), top_half + bottom_half)

            # Padded image location in the original image
            # objPadImageLocation = BoundingBox(roi_left, roi_bottom, roi_left + roi_width, roi_bottom + roi_height)
            #
            # return objPadImageLocation
            return roi_left, roi_bottom, roi_left + roi_width, roi_bottom + roi_height

        def edge_spacing_x_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Edge spacing X to take care of if search/target pad region goes out of bound.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: Edge spacing X.
            """
            output_width = compute_output_width_f(bbox_tight)
            bbox_center_x = get_center_x_f(bbox_tight)

            return torch.maximum(torch.tensor(0.0).to(self.device), (output_width / 2) - bbox_center_x)

        def edge_spacing_y_f(bbox_tight: "torch.Tensor") -> "torch.Tensor":
            """
            Edge spacing X to take care of if search/target pad region goes out of bound.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :return: Edge spacing X.
            """
            output_height = compute_output_height_f(bbox_tight)
            bbox_center_y = get_center_y_f(bbox_tight)

            return torch.maximum(torch.tensor(0.0).to(self.device), (output_height / 2) - bbox_center_y)

        def crop_pad_image(bbox_tight: "torch.Tensor", image: "torch.Tensor") -> tuple[
            "torch.Tensor",
            tuple["torch.Tensor", "torch.Tensor", "torch.Tensor", "torch.Tensor"],
            "torch.Tensor",
            "torch.Tensor",
        ]:
            """
            Around the bounding box, we define an extra context factor of 2, which we will crop from the original image.

            :param bbox_tight: Coordinates of bounding box [x1, y1, x2, y2].
            :param image: Frame to be cropped and padded.
            :return: Cropped and Padded image.
            """
            import math
            import torch

            pad_image_location = compute_crop_pad_image_location(bbox_tight, image)
            # roi_left = min(pad_image_location.x1, (image.shape[1] - 1))
            # roi_bottom = min(pad_image_location.y1, (image.shape[0] - 1))
            roi_left = torch.minimum(pad_image_location[0], torch.tensor(image.shape[1] - 1).to(self.device))
            roi_bottom = torch.minimum(pad_image_location[1], torch.tensor(image.shape[0] - 1).to(self.device))
            # roi_width = min(image.shape[1], max(1.0, math.ceil(pad_image_location.x2 - pad_image_location.x1)))
            # roi_height = min(image.shape[0], max(1.0, math.ceil(pad_image_location.y2 - pad_image_location.y1)))
            roi_width = min(image.shape[1], max(1, math.ceil(pad_image_location[2] - pad_image_location[0])))
            roi_height = min(image.shape[0], max(1, math.ceil(pad_image_location[3] - pad_image_location[1])))

            roi_bottom_int = int(roi_bottom)
            roi_bottom_height_int = roi_bottom_int + roi_height

            roi_left_int = int(roi_left)
            roi_left_width_int = roi_left_int + roi_width

            cropped_image = image[roi_bottom_int:roi_bottom_height_int, roi_left_int:roi_left_width_int]

            # output_width = max(math.ceil(bbox_tight.compute_output_width()), roi_width)
            # output_height = max(math.ceil(bbox_tight.compute_output_height()), roi_height)
            output_width = max(math.ceil(compute_output_width_f(bbox_tight)), roi_width)
            output_height = max(math.ceil(compute_output_height_f(bbox_tight)), roi_height)
            if image.ndim > 2:
                # output_image = np.zeros((int(output_height), int(output_width), image.shape[2]), dtype=image.dtype)
                output_image = torch.zeros((int(output_height), int(output_width), image.shape[2]), dtype=image.dtype)
            else:
                # output_image = np.zeros((int(output_height), int(output_width)), dtype=image.dtype)
                output_image = torch.zeros((int(output_height), int(output_width)), dtype=image.dtype)

            # edge_spacing_x = min(bbox_tight.edge_spacing_x(), (image.shape[1] - 1))
            # edge_spacing_y = min(bbox_tight.edge_spacing_y(), (image.shape[0] - 1))
            edge_spacing_x = torch.minimum(edge_spacing_x_f(bbox_tight), torch.tensor(image.shape[1] - 1))
            edge_spacing_y = torch.minimum(edge_spacing_y_f(bbox_tight), torch.tensor(image.shape[0] - 1))

            # rounding should be done to match the width and height
            output_image[
                int(edge_spacing_y) : int(edge_spacing_y) + cropped_image.shape[0],
                int(edge_spacing_x) : int(edge_spacing_x) + cropped_image.shape[1],
            ] = cropped_image

            return output_image, pad_image_location, edge_spacing_x, edge_spacing_y

        target_pad, _, _, _ = crop_pad_image(prev_bbox, prev_frame)
        cur_search_region, search_location, edge_spacing_x, edge_spacing_y = crop_pad_image(prev_bbox, curr_frame)

        target_pad_in = self._preprocess(target_pad).unsqueeze(0).to(self.device)
        cur_search_region_in = self._preprocess(cur_search_region).unsqueeze(0).to(self.device)

        pred_bb = self._model.forward(target_pad_in.float(), cur_search_region_in.float())

        pred_bb = torch.squeeze(pred_bb)

        k_scale_factor = 10
        height = cur_search_region.shape[0]
        width = cur_search_region.shape[1]

        # Normalize the image bounding box
        pred_bb[0] = pred_bb[0] / k_scale_factor * width
        pred_bb[2] = pred_bb[2] / k_scale_factor * width
        pred_bb[1] = pred_bb[1] / k_scale_factor * height
        pred_bb[3] = pred_bb[3] / k_scale_factor * height

        # brings gradients to zero
        # pred_bb = torch.round(pred_bb)

        # move the bounding box to target/search region coordinates
        raw_image = curr_frame
        pred_bb[0] = max(0.0, pred_bb[0] + search_location[0] - edge_spacing_x)
        pred_bb[1] = max(0.0, pred_bb[1] + search_location[1] - edge_spacing_y)
        pred_bb[2] = min(raw_image.shape[1], pred_bb[2] + search_location[0] - edge_spacing_x)
        pred_bb[3] = min(raw_image.shape[0], pred_bb[3] + search_location[1] - edge_spacing_y)

        # brings gradients to zero
        # pred_bb = torch.round(pred_bb)

        return pred_bb

        Track current frame.

        :param curr_frame: Current frame.
        :param prev_frame: Previous frame.
        :return: bounding box of previous frame
        
art/estimators/object_tracking/pytorch_goturn.py