예제 #1
0
    def convert_to_jp2(self) -> None:
        try:
            if not self.image_data.valid_image_ext():
                raise AviJp2ProcessorError('Source image is not a .tiff or .tif')

            kdu_args = self.__calculate_kdu_options() + self.__calculate_kdu_recipe()
            input_file = str(self.image_data.image_src_path)

            if self.image_data.src_quality == 'color':
                self.logger.debug('Adding icc profile to image')
                input_file = self.convert_icc_profile()
                self.logger.debug('Successfully added icc profile')

            self.logger.debug('Pre validating image at {}'.format(input_file))
            try:
                validation.check_image_suitable_for_jp2_conversion(
                    input_file, require_icc_profile_for_colour=True,
                    require_icc_profile_for_greyscale=False)
            except ValidationError as v_e:
                msg = f'ValidationError: {v_e}'
                raise AviJp2ProcessorError(msg) from v_e

            self.logger.debug('image {} is able to be converted to jp2!'.format(input_file))

            with Image.open(input_file) as input_pil:
                if input_pil.mode == 'RGBA':
                    if kakadu.ALPHA_OPTION not in kdu_args:
                        kdu_args += [kakadu.ALPHA_OPTION]

            self.logger.debug('Kakadu args are {}'.format(kdu_args))
            self.logger.debug('Preparing to output jp2...')
            try:
                self.kakadu.kdu_compress(input_file, self.destination_file, kakadu_options=kdu_args)
            except (KakaduError, OSError) as kdu_e:
                msg = f'{kdu_e.__class__.__name__} {kdu_e}'
                raise AviJp2ProcessorError(msg) from kdu_e
            finally:
                # Deletes the tmp file created from the convert_icc_profile method.
                if Path(input_file).exists() and input_file != str(self.image_data.image_src_path):
                    self.logger.debug('Removing {}'.format(input_file))
                    os.unlink(input_file)
            self.logger.debug('Successfully converted to jp2!')
            self.__set_success_result()
        except AviJp2ProcessorError as avi_ex:
            msg = str(avi_ex)
            self.__set_error_result(msg)
            self.logger.error('Error occured processing file for Jp2 conversion!')
            self.logger.error('Check result and logs for more details.')
예제 #2
0
    def test_creates_correct_files_greyscale(self):
        validation.check_image_suitable_for_jp2_conversion(filepaths.GREYSCALE_TIF,
                                                           require_icc_profile_for_greyscale=True)
        with temporary_folder() as output_folder:

            get_derivatives_generator().generate_derivatives_from_tiff(filepaths.GREYSCALE_TIF,
                                                                       output_folder, check_lossless=True,
                                                                       save_embedded_metadata=False)

            jpg_file = os.path.join(output_folder, 'full.jpg')
            jp2_file = os.path.join(output_folder, 'full_lossless.jp2')
            assert os.path.isfile(jpg_file)
            assert os.path.isfile(jp2_file)
            assert len(os.listdir(output_folder)) == 2
            assert image_files_match(jpg_file, filepaths.RESIZED_JPG_FROM_GREYSCALE_TIF)
            assert image_files_match(jp2_file, filepaths.LOSSLESS_JP2_FROM_GREYSCALE_TIF_XMP)
예제 #3
0
    def generate_derivatives_from_jpg(self,
                                      jpg_filepath,
                                      output_folder,
                                      save_embedded_metadata=True,
                                      check_lossless=True):
        """
        Extracts the embedded metadata, creates a copy of the JPEG file and a validated JPEG2000 file.
        Stores all in the given folder.

        :param jpg_filepath: The path to the source JPEG file.
        :param output_folder: The folder where the derivatives will be stored
        :param save_embedded_metadata: If true, metadata will be extracted from the image file and preserved in a separate xml file
        :param check_lossless: If true, check the created JPEG2000 file is visually identical to the TIFF created from the source file
        :return: filepaths of created files
        """
        self.log.debug("Processing {0}".format(jpg_filepath))
        self.log.info(
            "There may be some loss in converting from jpg to jpg2000, as jpg compression is lossy. "
            "The lossless check is against the tiff created from the jpg")
        source_file_name = os.path.basename(jpg_filepath)

        validation.check_image_suitable_for_jp2_conversion(
            jpg_filepath,
            require_icc_profile_for_colour=self.require_icc_profile_for_colour,
            require_icc_profile_for_greyscale=self.
            require_icc_profile_for_greyscale)

        output_jpg_filepath = os.path.join(
            output_folder,
            self._get_filename(DEFAULT_JPG_FILENAME, source_file_name))
        shutil.copy(jpg_filepath, output_jpg_filepath)
        generated_files = [output_jpg_filepath]

        if save_embedded_metadata:
            embedded_metadata_file_path = os.path.join(
                output_folder,
                self._get_filename(DEFAULT_EMBEDDED_METADATA_FILENAME,
                                   source_file_name))
            self.converter.extract_xmp_to_sidecar_file(
                jpg_filepath, embedded_metadata_file_path)
            self.log.debug('Extracted metadata file {0} generated'.format(
                embedded_metadata_file_path))
            generated_files += [embedded_metadata_file_path]

        with tempfile.NamedTemporaryFile(
                prefix='image-processing_',
                suffix='.tif') as scratch_tiff_file_obj:
            scratch_tiff_filepath = scratch_tiff_file_obj.name
            self.converter.convert_to_tiff(jpg_filepath, scratch_tiff_filepath)

            validation.check_colour_profiles_match(jpg_filepath,
                                                   scratch_tiff_filepath)

            lossless_filepath = os.path.join(
                output_folder,
                self._get_filename(DEFAULT_LOSSLESS_JP2_FILENAME,
                                   source_file_name))
            self.generate_jp2_from_tiff(scratch_tiff_filepath,
                                        lossless_filepath)
            self.validate_jp2_conversion(scratch_tiff_filepath,
                                         lossless_filepath,
                                         check_lossless=check_lossless)
            generated_files.append(lossless_filepath)

        self.log.debug(
            "Successfully generated derivatives for {0} in {1}".format(
                jpg_filepath, output_folder))

        return generated_files
예제 #4
0
    def generate_derivatives_from_tiff(self,
                                       tiff_filepath,
                                       output_folder,
                                       include_tiff=False,
                                       save_embedded_metadata=True,
                                       create_jpg_as_thumbnail=True,
                                       check_lossless=True):
        """
        Extracts the embedded metadata, creates a JPEG file and a validated JPEG2000 file.
        Stores all in the given folder.

        :param create_jpg_as_thumbnail: create the JPG as a resized thumbnail, not a high quality image.
            Parameters for resize and quality are set on a class level
        :param tiff_filepath:
        :param output_folder: the folder where the related dc.xml will be stored
        :param include_tiff: Include copy of source tiff file in derivatives
        :param save_embedded_metadata: If true, metadata will be extracted from the image file and preserved in a separate xml file
        :param check_lossless: If true, check the created jpg2000 file is visually identical to the source file
        :return: filepaths of created files
        """
        self.log.debug("Processing {0}".format(tiff_filepath))
        source_file_name = os.path.basename(tiff_filepath)

        validation.check_image_suitable_for_jp2_conversion(
            tiff_filepath,
            require_icc_profile_for_colour=self.require_icc_profile_for_colour,
            require_icc_profile_for_greyscale=self.
            require_icc_profile_for_greyscale)

        with Image.open(tiff_filepath) as tiff_pil:
            if tiff_pil.mode == 'RGBA':
                # some RGBA tiffs don't convert properly back from jp2 - kakadu warns about unassociated alpha channels
                check_lossless = True

        with tempfile.NamedTemporaryFile(prefix='image-processing_',
                                         suffix='.tif') as temp_tiff_file_obj:
            # only work from a temporary file if we need to - e.g. if the tiff filepath is invalid,
            # or if we need to normalise the tiff. Otherwise just use the original tiff
            temp_tiff_filepath = temp_tiff_file_obj.name
            if os.path.splitext(tiff_filepath)[1].lower() not in [
                    '.tif', '.tiff'
            ]:
                shutil.copy(tiff_filepath, temp_tiff_filepath)
                normalised_tiff_filepath = temp_tiff_filepath
            else:
                normalised_tiff_filepath = tiff_filepath

            jpeg_filepath = os.path.join(
                output_folder,
                self._get_filename(DEFAULT_JPG_FILENAME, source_file_name))

            jpg_quality = None if create_jpg_as_thumbnail else self.jpg_high_quality_value
            jpg_resize = self.jpg_thumbnail_resize_value if create_jpg_as_thumbnail else None

            self.converter.convert_to_jpg(normalised_tiff_filepath,
                                          jpeg_filepath,
                                          quality=jpg_quality,
                                          resize=jpg_resize)
            self.log.debug('jpeg file {0} generated'.format(jpeg_filepath))
            generated_files = [jpeg_filepath]

            if save_embedded_metadata:
                embedded_metadata_file_path = os.path.join(
                    output_folder,
                    self._get_filename(DEFAULT_EMBEDDED_METADATA_FILENAME,
                                       source_file_name))
                self.converter.extract_xmp_to_sidecar_file(
                    tiff_filepath, embedded_metadata_file_path)
                self.log.debug('Extracted metadata file {0} generated'.format(
                    embedded_metadata_file_path))
                generated_files += [embedded_metadata_file_path]

            if include_tiff:
                output_tiff_filepath = os.path.join(
                    output_folder,
                    self._get_filename(DEFAULT_TIFF_FILENAME,
                                       source_file_name))
                shutil.copy(tiff_filepath, output_tiff_filepath)
                generated_files += [output_tiff_filepath]

            lossless_filepath = os.path.join(
                output_folder,
                self._get_filename(DEFAULT_LOSSLESS_JP2_FILENAME,
                                   source_file_name))
            self.generate_jp2_from_tiff(normalised_tiff_filepath,
                                        lossless_filepath)
            self.validate_jp2_conversion(normalised_tiff_filepath,
                                         lossless_filepath,
                                         check_lossless=check_lossless)
            generated_files.append(lossless_filepath)

            self.log.debug(
                "Successfully generated derivatives for {0} in {1}".format(
                    tiff_filepath, output_folder))

            return generated_files