def _on_after_attack(self, attacked_image: Picture):
        """
        Instructions executed after performing the attack
        :return:
        """

        psnr = PSNR(self.target_image,np.array(attacked_image,np.int))

        path = os.path.join(self.debug_folder,"attacked_image.png")
        attacked_image.save(path)

        attacked_image = Picture(path=path)

        self.detector.prediction_pipeline(attacked_image, os.path.join(self.debug_folder, "final result"),original_picture=self.target_image,omask=self.target_image_mask,note="final result PSNR:{:.2f}".format(psnr))

        end_time = datetime.now()
        timedelta = end_time - self.start_time

        self.write_to_logs("Attack pipeline terminated in {}".format(timedelta))
    def _compute_target_representation(
            self, target_representation_source_image: Picture,
            target_representation_source_image_mask: Picture):
        """
        Generate the target representation executing the following steps:

            1) Divide the image into patches
            2) Select only the authentic patches
            3) Foreach patch compute its noiseptint
            4) Average all the noiseprint maps

        :return: the target representation in the shape of a numpy array
        """

        # format the target image in the standard that the noiseprint requires
        target_representation_source_image = prepare_image_noiseprint(
            target_representation_source_image)

        # spit the image into patches
        authentic_patches = target_representation_source_image.get_authentic_patches(
            target_representation_source_image_mask,
            self.patch_size,
            self.padding_size,
            force_shape=True,
            zero_padding=True)

        complete_patch_size = (self.patch_size[0] + self.padding_size[1] +
                               self.padding_size[3], self.patch_size[1] +
                               self.padding_size[0] + self.padding_size[2])

        # create target patch object
        target_patch = np.zeros(complete_patch_size)

        # create a map in which to store the used patches for visualization
        patches_map = np.zeros(target_representation_source_image.shape)

        # generate authentic target representation
        self.write_to_logs("Generating target representation...")

        # foreach authentic patch
        for original_patch in tqdm(authentic_patches):
            assert (original_patch.shape == target_patch.shape)

            # compute its noiseprint
            noiseprint_patch = np.squeeze(
                self._engine.model(original_patch[np.newaxis, :, :,
                                                  np.newaxis]))

            # add the noiseprint to the mean target patch object
            target_patch += noiseprint_patch / len(authentic_patches)

            # add the result to the map of patches
            patches_map = original_patch.no_paddings().add_to_image(
                patches_map)

        self.write_to_logs("Target representation generated")

        t_no_padding = authentic_patches[0].no_paddings(target_patch)

        # save a visualization of the target representation
        normalized_noiseprint = normalize_noiseprint_no_margins(t_no_padding)

        plt.imsave(fname=os.path.join(self.debug_folder, "image-target.png"),
                   arr=normalized_noiseprint,
                   cmap='gray',
                   format='png')

        visuallize_matrix_values(
            t_no_padding,
            os.path.join(self.debug_folder, "image-target-raw.png"))

        patches_map = Picture(patches_map)
        patches_map.save(os.path.join(self.debug_folder, "patches-map.png"))

        # save target representation t in a 8x8 grid for visualization purposes
        if t_no_padding.shape[0] % 8 == 0 and t_no_padding.shape[1] % 8 == 0:

            patch_8 = np.zeros((8, 8))
            n_patches8 = (t_no_padding.shape[0] //
                          8) * (t_no_padding.shape[1] // 8)
            for x in range(0, t_no_padding.shape[0], 8):
                for y in range(0, t_no_padding.shape[1], 8):
                    patch_8 += t_no_padding[x:x + 8, y:y + 8] / n_patches8

            visuallize_matrix_values(
                patch_8,
                os.path.join(self.debug_folder, "clean_target_patch.png"))

        return target_patch
    def _compute_target_representation(
            self, target_representation_source_image: Picture,
            target_representation_source_image_mask: Picture):
        """
        Generate the target representation executing the following steps:

            1) Generate an image wise noiseprint representation on the entire image
            2) Divide this noiseprint map into patches
            3) Average these patches
            4) Create an image wide target representation by tiling these patches together

        :return: the target representation in the shape of a numpy array
        """

        # conver the image in the standard required by noiseprint
        image = prepare_image_noiseprint(target_representation_source_image)

        # generate an image wise noiseprint representation on the entire image
        original_noiseprint = Picture(self._engine.predict(image))

        # cut away section along borders
        original_noiseprint[0:self.patch_size[0], :] = 0
        original_noiseprint[-self.patch_size[0]:, :] = 0
        original_noiseprint[:, 0:self.patch_size[1]] = 0
        original_noiseprint[:, -self.patch_size[1]:] = 0

        # exctract the authentic patches from the image
        authentic_patches = original_noiseprint.get_authentic_patches(
            target_representation_source_image_mask,
            self.patch_size,
            force_shape=True,
            zero_padding=False)

        # create target patch object
        target_patch = np.zeros(self.patch_size)

        patches_map = np.zeros(image.shape)

        for patch in tqdm(authentic_patches):
            assert (patch.clean_shape == target_patch.shape)

            target_patch += patch / len(authentic_patches)

            patches_map = patch.no_paddings().add_to_image(patches_map)

        # compute the tiling factors along the X and Y axis
        repeat_factors = (ceil(image.shape[0] / target_patch.shape[0]),
                          ceil(image.shape[1] / target_patch.shape[1]))

        # tile the target representations together
        image_target_representation = np.tile(target_patch, repeat_factors)

        # cut away "overflowing" margins
        image_target_representation = image_target_representation[:image.shape[
            0], :image.shape[1]]

        # save tile visualization
        visuallize_matrix_values(
            target_patch,
            os.path.join(self.debug_folder, "image-target-raw.png"))

        patches_map = Picture(normalize_noiseprint(patches_map))
        patches_map.save(os.path.join(self.debug_folder, "patches-map.png"))

        return Picture(image_target_representation)