def _generate_mask(
        self,
        x: np.ndarray,
        x_noise: np.ndarray,
        x_tar_noise: np.ndarray,
        mask: np.ndarray,
        y: int,
        obj_width: float,
        focal: float,
        clip_min: float,
        clip_max: float,
        pts: np.ndarray | None = None,
    ) -> np.ndarray:
        """
        Function to generate a mask.

        :param x: An array with one original input to be attacked.
        :param x_noise: x in the resolution of the noise size.
        :param x_tar_noise: x_tar in the resolution of the noise size.
        :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 y: The target label.
        :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 pts: Optional. A set of points that will set the crop size in the perspective transform.
        :return: A mask.
        """

        # Stage 1: HEATMAP: Collect all the valid patches and order by computed heatmap
        xforms = get_transform_params(
            self.num_xforms_mask,
            self.rotation_range,
            self.dist_range,
            self.gamma_range,
            self.crop_percent_range,
            self.off_x_range,
            self.off_y_range,
            self.blur_kernels,
            obj_width,
            focal,
        )

        object_size = mask.sum() / mask.shape[-1]

        patch = np.ones((self.heat_patch_size[1], self.heat_patch_size[0], x.shape[-1]))
        patches = []
        indices = []

        # collect all valid patches
        for i in range(0, mask.shape[0] - self.heat_patch_size[1] + 1, self.heat_patch_stride[1]):
            for j in range(0, mask.shape[1] - self.heat_patch_size[0] + 1, self.heat_patch_stride[0]):
                new_mask = np.zeros(mask.shape)
                new_mask[
                    i : min(i + self.heat_patch_size[1], mask.shape[0]),
                    j : min(j + self.heat_patch_size[0], mask.shape[1]),
                ] = patch
                new_mask = new_mask * mask
                if np.sum(new_mask) > 0:
                    patches.append(new_mask)
                    indices.append((i, j))

        # compute heatmap and order
        if self.heatmap_mode == "Random":
            tr_scores = [random.random() for i in range(len(patches))]
        else:  # Target mode

            tr_scores = self._get_heatmap(
                x,
                x_noise,
                x_tar_noise,
                mask,
                y,
                patches,
                xforms,
                clip_min,
                clip_max,
                pts,
            )

        tr_scores_np = np.asarray(tr_scores)
        order = tr_scores_np.argsort()
        patches = [patches[ind] for ind in order]
        indices = [indices[ind] for ind in order]

        # STAGE 2: Coarse Reduction
        best_mask, patches, indices = self._get_coarse_reduced_mask(
            x, x_noise, x_tar_noise, y, mask, patches, indices, xforms, clip_min, clip_max, pts
        )

        # STAGE 3: Fine Reduction
        if self.max_mask_size > 0:
            lbd = 5
            while best_mask.sum() / mask.shape[-1] > self.max_mask_size:
                patches_copy = list(patches)
                best_mask = self._get_fine_reduced_mask(
                    x,
                    x_noise,
                    x_tar_noise,
                    y,
                    best_mask,
                    patches_copy,
                    xforms,
                    object_size,
                    clip_min,
                    clip_max,
                    lbd,
                    pts,
                )
                lbd += 5
        else:
            best_mask = self._get_fine_reduced_mask(
                x,
                x_noise,
                x_tar_noise,
                y,
                best_mask,
                patches,
                xforms,
                object_size,
                clip_min,
                clip_max,
                pts=pts,
            )

        return best_mask

        Function to generate a mask.

        :param x: An array with one original input to be attacked.
        :param x_noise: x in the resolution of the noise size.
        :param x_tar_noise: x_tar in the resolution of the noise size.
        :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 y: The target label.
        :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 pts: Optional. A set of points that will set the crop size in the perspective transform.
        :return: A mask.
        
art/attacks/evasion/graphite/graphite_blackbox.py