def _perturb(
        self,
        x: np.ndarray,
        y: int,
        x_tar: np.ndarray,
        obj_width: float,
        focal: float,
        clip_min: float,
        clip_max: float,
        mask: np.ndarray | None = None,
        pts: np.ndarray | None = None,
    ) -> np.ndarray:
        """
        Internal attack function for one example.

        :param x: An array with one original input to be attacked.
        :param y: The target label.
        :param x_tar: Initial array to act as an example target image.
        :param obj_width: Estimated width of object in inches for perspective transform.
        :param focal: Estimated focal length in ft for perspective transform.
        :param clip_min: Minimum value of an example.
        :param clip_max: Maximum value of an example.
        :param mask: An array with a mask to be applied to the adversarial perturbations. Shape needs to be
                     broadcastable to the shape of x. Any features for which the mask is zero will not be adversarially
                     perturbed.
        :param pts: Optional. A set of points that will set the crop size in the perspective transform.
        :return: An adversarial example.
        """
        import cv2

        x = (x.copy() - clip_min) / (clip_max - clip_min)
        x_tar = (x_tar.copy() - clip_min) / (clip_max - clip_min)

        if mask is None:
            mask_array = np.ones((self.noise_size[1], self.noise_size[0], x.shape[2]))
        else:
            mask_array = mask
        mask_array = mask_array / np.max(mask_array)

        x_copy = x.copy()
        x_tar_copy = x_tar.copy()
        mask_copy = mask_array.copy()
        x_noise = cv2.resize(x_copy, self.noise_size)
        x_tar_noise = cv2.resize(x_tar_copy, self.noise_size)
        mask_noise = cv2.resize(mask_copy, self.noise_size).astype(float)
        mask_noise = np.where(mask_noise > 0.5, 1.0, 0.0)

        if len(x_noise.shape) < 3:
            x_noise = x_noise[:, :, np.newaxis]
        if len(x_tar_noise.shape) < 3:
            x_tar_noise = x_tar_noise[:, :, np.newaxis]
        if len(mask_noise.shape) < 3:
            mask_noise = mask_noise[:, :, np.newaxis]

        mask_out = self._generate_mask(
            x_copy, x_noise, x_tar_noise, mask_noise, y, obj_width, focal, clip_min, clip_max, pts
        )

        adversarial = self._boost(x_copy, x_noise, x_tar_noise, mask_out, y, obj_width, focal, clip_min, clip_max, pts)

        adversarial = np.clip(adversarial.copy() * (clip_max - clip_min) + clip_min, clip_min, clip_max)
        return adversarial

        Internal attack function for one example.

        :param x: An array with one original input to be attacked.
        :param y: The target label.
        :param x_tar: Initial array to act as an example target image.
        :param obj_width: Estimated width of object in inches for perspective transform.
        :param focal: Estimated focal length in ft for perspective transform.
        :param clip_min: Minimum value of an example.
        :param clip_max: Maximum value of an example.
        :param mask: An array with a mask to be applied to the adversarial perturbations. Shape needs to be
                     broadcastable to the shape of x. Any features for which the mask is zero will not be adversarially
                     perturbed.
        :param pts: Optional. A set of points that will set the crop size in the perspective transform.
        :return: An adversarial example.
        
art/attacks/evasion/graphite/graphite_blackbox.py