diff --git a/image_processing/conversion.py b/image_processing/conversion.py index 5324bbf..e47b0d6 100755 --- a/image_processing/conversion.py +++ b/image_processing/conversion.py @@ -47,11 +47,12 @@ def convert_to_jpg(self, input_filepath, output_filepath, resize=None, quality=N """ with Image.open(input_filepath) as input_pil: icc_profile = input_pil.info.get('icc_profile') - if input_pil.mode == 'RGBA': + if input_pil.mode in ['RGBA', 'RGBX']: self.logger.warning( - 'Image is RGBA - the alpha channel will be removed from the JPEG derivative image') + 'Image is %s - the fourth channel will be removed from the JPEG derivative image', input_pil.mode) input_pil = input_pil.convert(mode="RGB") if input_pil.mode == 'I;16': + # JPEG doesn't support 16bit self.logger.warning( 'Image is 16bpp - will be downsampled to 8bpp') input_pil = input_pil.convert(mode="RGB") diff --git a/image_processing/validation.py b/image_processing/validation.py index f5a9565..cd9ea96 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] +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 def validate_jp2(image_file, output_file=None): @@ -154,12 +154,9 @@ 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 in ['RGBX', 'I;16'] and converted_image.mode in ['RGB', 'RGBA']: - logger.info('Converted image in RGB space, but was converted from a compatible space. This is expected.') else: raise exceptions.ValidationError( - 'Converted file {0} has different colour mode from {1}' - .format(converted_filepath, source_filepath) + f'Converted file {converted_filepath} has different colour mode ({converted_image.mode}) from {source_filepath} ({source_image.mode})' ) source_icc = source_image.info.get('icc_profile') @@ -190,6 +187,7 @@ def check_image_suitable_for_jp2_conversion(image_filepath, require_icc_profile_ if colour_mode not in ACCEPTED_COLOUR_MODES: raise exceptions.ValidationError("Unsupported colour mode {0} for {1}".format(colour_mode, image_filepath)) + # todo: do I need to warn for RGBX too? if colour_mode == 'RGBA': # In some cases alpha channel data is stored in a way that means it would be lost in the conversion back to # tiff from jp2. diff --git a/tests/data/normal_map.tif b/tests/data/normal_map.tif old mode 100755 new mode 100644 index 6dffd4a..72c30bc Binary files a/tests/data/normal_map.tif and b/tests/data/normal_map.tif differ diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 43a416c..646a05a 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -51,33 +51,12 @@ def test_converts_depthmap_tif_to_jpeg(self): quality=derivative_files_generator.DEFAULT_JPG_HIGH_QUALITY_VALUE) assert os.path.isfile(output_file) - - @mark.skipif(not cmd_is_executable('/opt/kakadu/kdu_compress'), reason="requires kakadu installed") - def test_converts_depthmap_tif_to_jpeg2000(self): - with temporary_folder() as output_folder: - output_file = os.path.join(output_folder, 'output.jpg') - get_kakadu().kdu_compress(filepaths.DEPTHMAP_TIF, output_file, - kakadu_options=kakadu.DEFAULT_COMPRESS_OPTIONS + kakadu.LOSSLESS_OPTIONS) - assert os.path.isfile(output_file) - - def test_converts_normalmap_tif_to_jpeg(self): with temporary_folder() as output_folder: output_file = os.path.join(output_folder, 'output.jpg') conversion.Converter().convert_to_jpg(filepaths.NORMALMAP_TIF, output_file, resize=None, quality=derivative_files_generator.DEFAULT_JPG_HIGH_QUALITY_VALUE) assert os.path.isfile(output_file) - validation.check_colour_profiles_match(filepaths.NORMALMAP_TIF, output_file) - - - @mark.skipif(not cmd_is_executable('/opt/kakadu/kdu_compress'), reason="requires kakadu installed") - def test_converts_normalmap_tif_to_jpeg2000(self): - with temporary_folder() as output_folder: - output_file = os.path.join(output_folder, 'output.jpg') - get_kakadu().kdu_compress(filepaths.NORMALMAP_TIF, output_file, - kakadu_options=kakadu.DEFAULT_COMPRESS_OPTIONS + kakadu.LOSSLESS_OPTIONS) - assert os.path.isfile(output_file) - @mark.skipif(not cmd_is_executable('/opt/kakadu/kdu_compress'), reason="requires kakadu installed") def test_converts_tif_to_lossy_jpeg2000(self): diff --git a/tests/test_derivatives_generator.py b/tests/test_derivatives_generator.py index c39caf1..3ed061c 100644 --- a/tests/test_derivatives_generator.py +++ b/tests/test_derivatives_generator.py @@ -225,3 +225,28 @@ def test_creates_correct_files_from_jpg(self): assert image_files_match(jpg_file, filepaths.STANDARD_JPG) 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): + """ + Check transformations of 16 bit depthmap 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') + generator.generate_jp2_from_tiff(filepaths.DEPTHMAP_TIF, output_file) + 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): + """ + Check transformations of RGBA 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 + 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 0d0b322..309c007 100644 --- a/tests/test_utils/filepaths.py +++ b/tests/test_utils/filepaths.py @@ -2,8 +2,8 @@ # 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' -NORMALMAP_TIF = 'tests/data/normal_map.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'