diff --git a/docs/jp2_colour_management.rst b/docs/jp2_colour_management.rst index a76cafc..87bbef9 100644 --- a/docs/jp2_colour_management.rst +++ b/docs/jp2_colour_management.rst @@ -58,6 +58,11 @@ The ``kdu_compress`` :ref:`command options ` we use do not As very few of our input images have an alpha channel, and we have not encountered failing cases in live data (as opposed to constructed test data), this is not a priority for us. We just make sure to check RGBA JP2 conversions are lossless, so we will catch any future failing cases. +RGBX colour mode +~~~~~~~~~~~~~~~~~~ + +I could not find a simple way to convert an RGBX TIFF to an RGBX JP2, though it's possible more work would produce one. In our particular use case it's always clear whether the image should be interpreted as RGBA or RGBX, so we convert RGBX images to RGBA, preserving all of the pixel data but not the colour mode. The program logs a warning when converting RGBX files because of this. + Colour profiles / modes not supported by JP2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/image_processing/derivative_files_generator.py b/image_processing/derivative_files_generator.py index 5ae1a51..5eb49c1 100644 --- a/image_processing/derivative_files_generator.py +++ b/image_processing/derivative_files_generator.py @@ -216,6 +216,11 @@ def generate_jp2_from_tiff(self, tiff_file, jp2_filepath): if tiff_pil.mode == 'RGBA': if kakadu.ALPHA_OPTION not in kakadu_options: kakadu_options += [kakadu.ALPHA_OPTION] + elif tiff_pil.mode == 'RGBX': + self.log.warning('Input tiff has colour mode RGBX. It will be converted to RGBA') + if kakadu.ALPHA_OPTION not in kakadu_options: + kakadu_options += [kakadu.ALPHA_OPTION] + self.kakadu.kdu_compress(tiff_file, jp2_filepath, kakadu_options=kakadu_options) self.log.debug('Lossless jp2 file {0} generated'.format(jp2_filepath)) diff --git a/image_processing/validation.py b/image_processing/validation.py index cd9ea96..beeb4b6 100755 --- a/image_processing/validation.py +++ b/image_processing/validation.py @@ -14,7 +14,7 @@ GREYSCALE = 'L' BITONAL = '1' MONOTONE_COLOUR_MODES = [GREYSCALE, BITONAL] -ACCEPTED_COLOUR_MODES = ['RGB', 'RGBA', 'RGBX', 'I;16', GREYSCALE, BITONAL] # todo: are there any other modes we may have to consider? Need to add test for RGBX image +ACCEPTED_COLOUR_MODES = ['RGB', 'RGBA', 'RGBX', 'I;16', GREYSCALE, BITONAL] def validate_jp2(image_file, output_file=None): @@ -154,6 +154,8 @@ def check_colour_profiles_match(source_filepath, converted_filepath): if source_image.mode != converted_image.mode: if source_image.mode == BITONAL and converted_image.mode == GREYSCALE: logger.info('Converted image is greyscale, not bitonal. This is expected') + elif source_image.mode == 'RGBX' and converted_image.mode == 'RGBA': + logger.info('Converted image in RGBA space, but was converted from RGBX. This is expected.') else: raise exceptions.ValidationError( f'Converted file {converted_filepath} has different colour mode ({converted_image.mode}) from {source_filepath} ({source_image.mode})' diff --git a/tests/data/normal_map.tif b/tests/data/normal_map.tif index 72c30bc..6dffd4a 100644 Binary files a/tests/data/normal_map.tif and b/tests/data/normal_map.tif differ diff --git a/tests/test_derivatives_generator.py b/tests/test_derivatives_generator.py index 3ed061c..86dd4c5 100644 --- a/tests/test_derivatives_generator.py +++ b/tests/test_derivatives_generator.py @@ -226,7 +226,7 @@ def test_creates_correct_files_from_jpg(self): assert image_files_match(jp2_file, filepaths.LOSSLESS_JP2_FROM_STANDARD_JPG_XMP) assert xmp_files_match(embedded_metadata_file, filepaths.STANDARD_JPG_XMP) - def test_converts_depthmap_tif_to_jpeg2000_losslessly(self): + def test_converts_i_16_tif_to_jpeg2000_losslessly(self): """ Check transformations of 16 bit depthmap images do not lose data :return: @@ -238,15 +238,15 @@ def test_converts_depthmap_tif_to_jpeg2000_losslessly(self): assert os.path.isfile(output_file) generator.check_conversion_was_lossless(filepaths.DEPTHMAP_TIF, output_file) - def test_converts_normalmap_tif_to_jpeg2000_losslessly(self): + def test_converts_rgbx_tif_to_jpeg2000_losslessly(self): """ - Check transformations of RGBA normal map images do not lose data + Check transformations of RGBX normal map images do not lose data :return: """ with temporary_folder() as output_folder: generator = get_derivatives_generator() output_file = os.path.join(output_folder, 'output.jp2') - # note that this method has specific handling for files with alpha channels that the direct converter method does not + # note that this method has specific handling for files with four channels that the direct converter method does not generator.generate_jp2_from_tiff(filepaths.NORMALMAP_TIF, output_file) assert os.path.isfile(output_file) generator.check_conversion_was_lossless(filepaths.NORMALMAP_TIF, output_file) diff --git a/tests/test_utils/filepaths.py b/tests/test_utils/filepaths.py index 309c007..7344f8e 100644 --- a/tests/test_utils/filepaths.py +++ b/tests/test_utils/filepaths.py @@ -2,8 +2,6 @@ # adobe rgb 1998 8 bit tiff file (our most common input format) + expected derivatives STANDARD_TIF = 'tests/data/standard_adobe.tif' -DEPTHMAP_TIF = 'tests/data/depth_map.tif' # 16 bit (I;16) archiox image -NORMALMAP_TIF = 'tests/data/normal_map.tif' # RGBA archiox image STANDARD_TIF_SINGLE_LAYER = 'tests/data/standard_adobe_tif_single_layer.tif' # same tif but without thumbnail LOSSY_JP2_FROM_STANDARD_TIF = 'tests/data/standard_adobe_tif_lossy.jp2' LOSSLESS_JP2_FROM_STANDARD_TIF_XMP = 'tests/data/standard_adobe_tif_xmp.jp2' @@ -51,6 +49,10 @@ NO_PROFILE_TIF = 'tests/data/no_profile.tif' +# tifs from our archiox project, which can have unusual colour modes +DEPTHMAP_TIF = 'tests/data/depth_map.tif' # 16 bit (I;16) archiox image +NORMALMAP_TIF = 'tests/data/normal_map.tif' # RGBX archiox image (compressed, as decompression with Image Magick converts it to RGBA) + # just truncated files INVALID_JP2 = 'tests/data/invalid.jp2' INVALID_TIF = 'tests/data/invalid.tif'