def test_auxiliary_channels_isolated(self): # test data in aux channels does not affect non-aux channels aux_channel_formats = [ # format, profile, color-only format, source test image ('RGBA', 'sRGB', 'RGB', hopper('RGBA')), ('RGBX', 'sRGB', 'RGB', hopper('RGBX')), ('LAB', 'LAB', 'LAB', Image.open('Tests/images/hopper.Lab.tif')), ] for src_format in aux_channel_formats: for dst_format in aux_channel_formats: for transform_in_place in [True, False]: # inplace only if format doesn't change if transform_in_place and src_format[0] != dst_format[0]: continue # convert with and without AUX data, test colors are equal source_profile = ImageCms.createProfile(src_format[1]) destination_profile = ImageCms.createProfile(dst_format[1]) source_image = src_format[3] test_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[0], outMode=dst_format[0]) # test conversion from aux-ful source if transform_in_place: test_image = source_image.copy() ImageCms.applyTransform(test_image, test_transform, inPlace=True) else: test_image = ImageCms.applyTransform(source_image, test_transform, inPlace=False) # reference conversion from aux-less source reference_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[2], outMode=dst_format[2]) reference_image = ImageCms.applyTransform(source_image.convert(src_format[2]), reference_transform) self.assert_image_equal(test_image.convert(dst_format[2]), reference_image)
def test_lab_roundtrip(): # check to see if we're at least internally consistent. pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") i = ImageCms.applyTransform(lena(), t) out = ImageCms.applyTransform(i, t2) assert_image_similar(lena(), out, 2)
def test_lab_roundtrip(self): # check to see if we're at least internally consistent. pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") i = ImageCms.applyTransform(hopper(), t) self.assertEqual(i.info['icc_profile'], ImageCmsProfile(pLab).tobytes()) out = ImageCms.applyTransform(i, t2) self.assert_image_similar(hopper(), out, 2)
def test_lab_roundtrip(self): # check to see if we're at least internally consistent. pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") i = ImageCms.applyTransform(lena(), t) self.assertEqual(i.info['icc_profile'], ImageCmsProfile(pLab).tobytes()) out = ImageCms.applyTransform(i, t2) self.assert_image_similar(lena(), out, 2)
def test_lab_roundtrip(): # check to see if we're at least internally consistent. psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") i = ImageCms.applyTransform(hopper(), t) assert i.info["icc_profile"] == ImageCmsProfile(pLab).tobytes() out = ImageCms.applyTransform(i, t2) assert_image_similar(hopper(), out, 2)
def get_transform(from_name, to_name, to_intent='perceptual', proof_name=None, proof_intent=None, use_black_pt=False): global icc_transform if not have_cms: return ColorManagerError("Either pillow is not installed, or there is " "no ICC support in this version of pillow") flags = 0 if proof_name is not None: if hasattr(ImageCms, 'FLAGS'): # supporting multiple versions of lcms...sigh.. flags |= ImageCms.FLAGS['SOFTPROOFING'] else: flags |= ImageCms.SOFTPROOFING if use_black_pt: if hasattr(ImageCms, 'FLAGS'): flags |= ImageCms.FLAGS['BLACKPOINTCOMPENSATION'] else: flags |= ImageCms.BLACKPOINTCOMPENSATION key = get_transform_key(from_name, to_name, to_intent, proof_name, proof_intent, flags) try: output_transform = icc_transform[key] except KeyError: # try to build transform on the fly try: if proof_name is not None: output_transform = ImageCms.buildProofTransform( profile[from_name].path, profile[to_name].path, profile[proof_name].path, 'RGB', 'RGB', renderingIntent=intents[to_intent], proofRenderingIntent=intents[proof_intent], flags=flags) else: output_transform = ImageCms.buildTransform( profile[from_name].path, profile[to_name].path, 'RGB', 'RGB', renderingIntent=intents[to_intent], flags=flags) icc_transform[key] = output_transform except Exception as e: raise ColorManagerError( "Failed to build profile transform: {!r}".format(e)) return output_transform
def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): def create_test_image(): # set up test image with something interesting in the tested aux # channel. nine_grid_deltas = [ (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), ] chans = [] bands = ImageMode.getmode(mode).bands for band_ndx in range(len(bands)): channel_type = 'L' # 8-bit unorm channel_pattern = hopper(channel_type) # paste pattern with varying offsets to avoid correlation # potentially hiding some bugs (like channels getting mixed). paste_offset = (int(band_ndx / float(len(bands)) * channel_pattern.size[0]), int(band_ndx / float(len(bands) * 2) * channel_pattern.size[1])) channel_data = Image.new(channel_type, channel_pattern.size) for delta in nine_grid_deltas: channel_data.paste( channel_pattern, tuple(paste_offset[c] + delta[c] * channel_pattern.size[c] for c in range(2))) chans.append(channel_data) return Image.merge(mode, chans) source_image = create_test_image() source_image_aux = source_image.getchannel(preserved_channel) # create some transform, it doesn't matter which one source_profile = ImageCms.createProfile("sRGB") destination_profile = ImageCms.createProfile("sRGB") t = ImageCms.buildTransform(source_profile, destination_profile, inMode=mode, outMode=mode) # apply transform if transform_in_place: ImageCms.applyTransform(source_image, t, inPlace=True) result_image = source_image else: result_image = ImageCms.applyTransform(source_image, t, inPlace=False) result_image_aux = result_image.getchannel(preserved_channel) self.assert_image_equal(source_image_aux, result_image_aux)
def test_sanity(): # basic smoke test. # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings assert_equal(v[0], '1.0.0 pil') assert_equal(list(map(type, v)), [str, str, str, str]) # internal version number assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") i = ImageCms.profileToProfile(lena(), SRGB, SRGB) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") assert_equal(t.inputMode, "RGB") assert_equal(t.outputMode, "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) # test PointTransform convenience API im = lena().point(t)
def make_profile_transform(profiles, modes, intent, prefer_embedded=True): transform = ImageCms.buildTransform(*profiles, *modes, intent) def profile_conversion(image): maybe_icc = image.info.get("icc_profile") if not prefer_embedded or maybe_icc is None: return ImageCms.applyTransform(image, transform) em_profile = ImageCms.ImageCmsProfile(io.BytesIO(maybe_icc)) return ImageCms.profileToProfile(image, em_profile, profiles[1], renderingIntent=intent, outputMode=modes[1]) return profile_conversion
def rgb_to_lab(im): rgb = ImageCms.createProfile(colorSpace='sRGB') lab = ImageCms.createProfile(colorSpace='LAB') transform = ImageCms.buildTransform(inputProfile=rgb, outputProfile=lab, inMode='RGB', outMode='LAB') return ImageCms.applyTransform(im=im, transform=transform)
def test_exceptions(): # Test mode mismatch psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") with pytest.raises(ValueError): t.apply_in_place(hopper("RGBA")) # the procedural pyCMS API uses PyCMSError for all sorts of errors with hopper() as im: with pytest.raises(ImageCms.PyCMSError): ImageCms.profileToProfile(im, "foo", "bar") with pytest.raises(ImageCms.PyCMSError): ImageCms.buildTransform("foo", "bar", "RGB", "RGB") with pytest.raises(ImageCms.PyCMSError): ImageCms.getProfileName(None) skip_missing() with pytest.raises(ImageCms.PyCMSError): ImageCms.isIntentSupported(SRGB, None, None)
def test_sanity(self): # basic smoke test. # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings self.assertEqual(v[0], '1.0.0 pil') self.assertEqual(list(map(type, v)), [str, str, str, str]) # internal version number self.assertRegexpMatches(ImageCms.core.littlecms_version, r"\d+\.\d+$") self.skip_missing() i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) self.assert_image(i, "RGB", (128, 128)) i = hopper() ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) self.assert_image(i, "RGB", (128, 128)) t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) self.assert_image(i, "RGB", (128, 128)) i = hopper() t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") ImageCms.applyTransform(hopper(), t, inPlace=True) self.assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) self.assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") self.assertEqual(t.inputMode, "RGB") self.assertEqual(t.outputMode, "RGB") i = ImageCms.applyTransform(hopper(), t) self.assert_image(i, "RGB", (128, 128)) # test PointTransform convenience API hopper().point(t)
def test_sanity(): # basic smoke test. # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings assert v[0] == "1.0.0 pil" assert list(map(type, v)) == [str, str, str, str] # internal version number assert re.search(r"\d+\.\d+(\.\d+)?$", features.version_module("littlecms2")) skip_missing() i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) assert_image(i, "RGB", (128, 128)) i = hopper() ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) assert_image(i, "RGB", (128, 128)) with hopper() as i: t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") ImageCms.applyTransform(hopper(), t, inPlace=True) assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") assert t.inputMode == "RGB" assert t.outputMode == "RGB" i = ImageCms.applyTransform(hopper(), t) assert_image(i, "RGB", (128, 128)) # test PointTransform convenience API hopper().point(t)
def test_lab_srgb(): pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") img = Image.open('Tests/images/lena.Lab.tif') img_srgb = ImageCms.applyTransform(img, t) # img_srgb.save('temp.srgb.tif') # visually verified vs ps. assert_image_similar(lena(), img_srgb, 30)
def test_exceptions(): # the procedural pyCMS API uses PyCMSError for all sorts of errors assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) assert_exception( ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
def test_lab_color(): psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") # Need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, and # have that mapping work back to a PIL mode (likely RGB). i = ImageCms.applyTransform(hopper(), t) assert_image(i, "LAB", (128, 128)) # i.save('temp.lab.tif') # visually verified vs PS. assert_image_similar_tofile(i, "Tests/images/hopper.Lab.tif", 3.5)
def test_lab_color(): pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") # need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, # and have that mapping work back to a PIL mode. (likely RGB) i = ImageCms.applyTransform(lena(), t) assert_image(i, "LAB", (128, 128)) # i.save('temp.lab.tif') # visually verified vs PS. target = Image.open('Tests/images/lena.Lab.tif') assert_image_similar(i, target, 30)
def test_exceptions(self): # the procedural pyCMS API uses PyCMSError for all sorts of errors self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(hopper(), "foo", "bar")) self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) self.assertRaises(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) self.skip_missing() self.assertRaises(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): def create_test_image(): # set up test image with something interesting in the tested aux # channel. nine_grid_deltas = [ # noqa: E128 (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), ] chans = [] bands = ImageMode.getmode(mode).bands for band_ndx in range(len(bands)): channel_type = 'L' # 8-bit unorm channel_pattern = hopper(channel_type) # paste pattern with varying offsets to avoid correlation # potentially hiding some bugs (like channels getting mixed). paste_offset = ( int(band_ndx / float(len(bands)) * channel_pattern.size[0]), int(band_ndx / float(len(bands) * 2) * channel_pattern.size[1]) ) channel_data = Image.new(channel_type, channel_pattern.size) for delta in nine_grid_deltas: channel_data.paste( channel_pattern, tuple(paste_offset[c] + delta[c] * channel_pattern.size[c] for c in range(2)), ) chans.append(channel_data) return Image.merge(mode, chans) source_image = create_test_image() source_image_aux = source_image.getchannel(preserved_channel) # create some transform, it doesn't matter which one source_profile = ImageCms.createProfile("sRGB") destination_profile = ImageCms.createProfile("sRGB") t = ImageCms.buildTransform( source_profile, destination_profile, inMode=mode, outMode=mode) # apply transform if transform_in_place: ImageCms.applyTransform(source_image, t, inPlace=True) result_image = source_image else: result_image = ImageCms.applyTransform( source_image, t, inPlace=False) result_image_aux = result_image.getchannel(preserved_channel) self.assert_image_equal(source_image_aux, result_image_aux)
def get_transform(from_name, to_name, to_intent='perceptual', proof_name=None, proof_intent=None, use_black_pt=False): global icc_transform flags = 0 if proof_name is not None: if hasattr(ImageCms, 'FLAGS'): # supporting multiple versions of lcms...sigh.. flags |= ImageCms.FLAGS['SOFTPROOFING'] else: flags |= ImageCms.SOFTPROOFING if use_black_pt: if hasattr(ImageCms, 'FLAGS'): flags |= ImageCms.FLAGS['BLACKPOINTCOMPENSATION'] else: flags |= ImageCms.BLACKPOINTCOMPENSATION key = get_transform_key(from_name, to_name, to_intent, proof_name, proof_intent, flags) try: output_transform = icc_transform[key] except KeyError: # try to build transform on the fly try: if not (proof_name is None): output_transform = ImageCms.buildProofTransform( profile[from_name].path, profile[to_name].path, profile[proof_name].path, 'RGB', 'RGB', renderingIntent=intents[to_intent], proofRenderingIntent=intents[proof_intent], flags=flags) else: output_transform = ImageCms.buildTransform( profile[from_name].path, profile[to_name].path, 'RGB', 'RGB', renderingIntent=intents[to_intent], flags=flags) icc_transform[key] = output_transform except Exception as e: raise Exception("Failed to build profile transform: %s" % (str(e))) return output_transform
def test_exceptions(self): # the procedural pyCMS API uses PyCMSError for all sorts of errors self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) self.assertRaises( ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
def test_lab_srgb(self): pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") img = Image.open('Tests/images/lena.Lab.tif') img_srgb = ImageCms.applyTransform(img, t) # img_srgb.save('temp.srgb.tif') # visually verified vs ps. self.assert_image_similar(lena(), img_srgb, 30) self.assertTrue(img_srgb.info['icc_profile']) profile = ImageCmsProfile(BytesIO(img_srgb.info['icc_profile'])) self.assertTrue('sRGB' in ImageCms.getProfileDescription(profile))
def test_lab_srgb(self): psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") with Image.open("Tests/images/hopper.Lab.tif") as img: img_srgb = ImageCms.applyTransform(img, t) # img_srgb.save('temp.srgb.tif') # visually verified vs ps. self.assert_image_similar(hopper(), img_srgb, 30) self.assertTrue(img_srgb.info["icc_profile"]) profile = ImageCmsProfile(BytesIO(img_srgb.info["icc_profile"])) self.assertIn("sRGB", ImageCms.getProfileDescription(profile))
def get_transform(from_name, to_name, to_intent='perceptual', proof_name=None, proof_intent=None, use_black_pt=False): global icc_transform flags = 0 if not (proof_name is None): flags |= ImageCms.SOFTPROOFING if use_black_pt: flags |= ImageCms.BLACKPOINTCOMPENSATION key = get_transform_key(from_name, to_name, to_intent, proof_name, proof_intent, flags) try: output_transform = icc_transform[key] except KeyError: # try to build transform on the fly try: if not (proof_name is None): output_transform = ImageCms.buildProofTransform( profile[from_name], profile[to_name], profile[proof_name], 'RGB', 'RGB', renderingIntent=intents[to_intent], proofRenderingIntent=intents[proof_intent], flags=flags) else: output_transform = ImageCms.buildTransform( profile[from_name], profile[to_name], 'RGB', 'RGB', renderingIntent=intents[to_intent], flags=flags) icc_transform[key] = output_transform except Exception as e: raise Exception("Failed to build profile transform: %s" % (str(e))) return output_transform
def test_exceptions(self): # Test mode mismatch psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA")) # the procedural pyCMS API uses PyCMSError for all sorts of errors self.assertRaises(ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar") self.assertRaises(ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB") self.assertRaises(ImageCms.PyCMSError, ImageCms.getProfileName, None) self.skip_missing() self.assertRaises(ImageCms.PyCMSError, ImageCms.isIntentSupported, SRGB, None, None)
def test_lab_color(self): psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") # Need to add a type mapping for some PIL type to TYPE_Lab_8 in # findLCMSType, and have that mapping work back to a PIL mode # (likely RGB). i = ImageCms.applyTransform(hopper(), t) self.assert_image(i, "LAB", (128, 128)) # i.save('temp.lab.tif') # visually verified vs PS. target = Image.open('Tests/images/hopper.Lab.tif') self.assert_image_similar(i, target, 3.5)
def test_simple_lab(): i = Image.new('RGB', (10, 10), (128, 128, 128)) pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) assert_equal(i_lab.mode, 'LAB') k = i_lab.getpixel((0, 0)) assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128 L = i_lab.getdata(0) a = i_lab.getdata(1) b = i_lab.getdata(2) assert_equal(list(L), [137] * 100) assert_equal(list(a), [128] * 100) assert_equal(list(b), [128] * 100)
def convert(self, image): source_profile = self.get_image_profile(image) if source_profile is not None: destination_profile = self.profile() logging.debug( f"Transforming color profiles. Source: {self.profile_name(source_profile)}, Destination: {self.profile_name(destination_profile)} " ) transform = ImageCms.buildTransform( inputProfile=source_profile, outputProfile=destination_profile, inMode=image.mode, outMode=self.config_entry.mode, renderingIntent=ImageCms.INTENT_RELATIVE_COLORIMETRIC) ImageCms.applyTransform(image, transform) else: logging.debug( f"Converting color modes. Source: {image.mode}, Destination: {self.config_entry.mode}" ) image = image.convert(mode=self.config_entry.mode) return image
def test_exceptions(self): # Test mode mismatch psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA")) # the procedural pyCMS API uses PyCMSError for all sorts of errors self.assertRaises( ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar") self.assertRaises( ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB") self.assertRaises( ImageCms.PyCMSError, ImageCms.getProfileName, None) self.skip_missing() self.assertRaises( ImageCms.PyCMSError, ImageCms.isIntentSupported, SRGB, None, None)
def _build_icc_transform(icc_profile: bytes) -> ImageCmsTransform: """Builds an ICC Transformation object. Parameters ---------- icc_profile: bytes ICC Profile Returns ------- PIL.ImageCms.ImageCmsTransform ICC Transformation object """ profile: bytes try: profile = ImageCmsProfile(BytesIO(icc_profile)) except OSError: raise ValueError('Cannot read ICC Profile in image metadata.') name = getProfileName(profile).strip() description = getProfileDescription(profile).strip() logger.debug(f'found ICC Profile "{name}": "{description}"') logger.debug('build ICC Transform') intent = ImageCms.INTENT_RELATIVE_COLORIMETRIC if not isIntentSupported( profile, intent=intent, direction=ImageCms.DIRECTION_INPUT ): raise ValueError( 'ICC Profile does not support desired ' 'color transformation intent.' ) return ImageCms.buildTransform( inputProfile=profile, outputProfile=ImageCms.createProfile('sRGB'), inMode='RGB', # according to PS3.3 C.11.15.1.1 outMode='RGB' )
def test_simple_lab(): i = Image.new("RGB", (10, 10), (128, 128, 128)) psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) assert i_lab.mode == "LAB" k = i_lab.getpixel((0, 0)) # not a linear luminance map. so L != 128: assert k == (137, 128, 128) l_data = i_lab.getdata(0) a_data = i_lab.getdata(1) b_data = i_lab.getdata(2) assert list(l_data) == [137] * 100 assert list(a_data) == [128] * 100 assert list(b_data) == [128] * 100
def test_simple_lab(self): i = Image.new('RGB', (10, 10), (128, 128, 128)) psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) self.assertEqual(i_lab.mode, 'LAB') k = i_lab.getpixel((0, 0)) # not a linear luminance map. so L != 128: self.assertEqual(k, (137, 128, 128)) l_data = i_lab.getdata(0) a_data = i_lab.getdata(1) b_data = i_lab.getdata(2) self.assertEqual(list(l_data), [137] * 100) self.assertEqual(list(a_data), [128] * 100) self.assertEqual(list(b_data), [128] * 100)
def test_simple_lab(self): i = Image.new("RGB", (10, 10), (128, 128, 128)) psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) self.assertEqual(i_lab.mode, "LAB") k = i_lab.getpixel((0, 0)) # not a linear luminance map. so L != 128: self.assertEqual(k, (137, 128, 128)) l_data = i_lab.getdata(0) a_data = i_lab.getdata(1) b_data = i_lab.getdata(2) self.assertEqual(list(l_data), [137] * 100) self.assertEqual(list(a_data), [128] * 100) self.assertEqual(list(b_data), [128] * 100)
def test_sanity(): # basic smoke test. # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings assert_equal(v[0], '0.1.0 pil') assert_equal(map(type, v), [str, str, str, str]) # internal version number assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") i = ImageCms.profileToProfile(lena(), SRGB, SRGB) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") assert_equal(t.inputMode, "RGB") assert_equal(t.outputMode, "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) # get profile information for file assert_equal( ImageCms.getProfileName(SRGB).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') assert_equal( ImageCms.getProfileInfo(SRGB).splitlines(), [ 'sRGB IEC61966-2.1', '', 'Copyright (c) 1998 Hewlett-Packard Company', '', 'WhitePoint : D65 (daylight)', '', 'Tests/icc/sRGB.icm' ]) assert_equal(ImageCms.getDefaultIntent(SRGB), 0) assert_equal( ImageCms.isIntentSupported(SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) # same, using profile object p = ImageCms.createProfile("sRGB") assert_equal( ImageCms.getProfileName(p).strip(), 'sRGB built-in - (lcms internal)') assert_equal( ImageCms.getProfileInfo(p).splitlines(), ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) assert_equal(ImageCms.getDefaultIntent(p), 0) assert_equal( ImageCms.isIntentSupported(p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) # extensions i = Image.open("Tests/images/rgb.jpg") p = ImageCms.getOpenProfile(StringIO(i.info["icc_profile"])) assert_equal( ImageCms.getProfileName(p).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') # the procedural pyCMS API uses PyCMSError for all sorts of errors assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) assert_exception( ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) # test PointTransform convenience API im = lena().point(t) # try fetching the profile for the current display device assert_no_exception(lambda: ImageCms.get_display_profile())
result = result.replace('&', '8') result = result.replace('S', '5') result = result.replace(' ', '') result = result[:5] result = result.lower() return result im = Image.open('captcha.png') # Convert to L*a*b colorspace is more complex: rgb = ImageCms.createProfile(colorSpace='sRGB') lab = ImageCms.createProfile(colorSpace='LAB') transform = ImageCms.buildTransform(inputProfile=rgb, outputProfile=lab, inMode='RGB', outMode='LAB') lab_im = ImageCms.applyTransform(im=im, transform=transform) lab_im = rescale(lab_im) l, a, b = lab_im.split() # Convert to numpy array and apply the threshold to remove lines np_a = np.array(a) threshold = 180 np_a[np_a < threshold] = 0 np_a[np_a > threshold] = 255 # Invert the image: we want black text on a white background np_a = 255 - np_a
profname, ext = os.path.splitext(filename) profile[profname] = os.path.join(basedir, "profiles", filename) rendering_intent = 0 # Prepare common transforms transform = {} # Build transforms for profile conversions for which we have profiles if have_cms: rendering_intent = ImageCms.INTENT_PERCEPTUAL for inprof, outprof in [('sRGB', 'working'), ('AdobeRGB', 'working'), ('working', 'monitor')]: if os.path.exists(profile[inprof]) and os.path.exists(profile[outprof]): transform[(inprof, outprof)] = ImageCms.buildTransform(profile[inprof], profile[outprof], 'RGB', 'RGB', renderingIntent=rendering_intent, flags=0) # For testing... #have_qtimage = False #have_pilutil = False #have_pil = False #have_cms = False class RGBFileHandler(object): def __init__(self, logger): self.logger = logger
def load_pil(self, pil_img): """ Load an already opened PIL image. Parameters ---------- pil_img : :class:`PIL.Image.Image` The image. Returns ------- A new :class:`Image`. Notes ----- This function should be used instead of :meth:`Image.from_pil` because may postprocess the image in various ways, depending on the loader configuration. """ # If we're cropping, do it. if self.crop is not None: upper = self.crop[0] right = pil_img.width - self.crop[1] lower = pil_img.height - self.crop[2] left = self.crop[3] # This operation can trigger PIL decompression-bomb aborts, so # avoid them in the standard, non-thread-safe, way. old_max = pil_image.MAX_IMAGE_PIXELS try: pil_image.MAX_IMAGE_PIXELS = None pil_img = pil_img.crop((left, upper, right, lower)) finally: pil_image.MAX_IMAGE_PIXELS = old_max # If an unrecognized mode, try to standardize it. The weird PIL modes # don't generally support an alpha channel, so we convert to RGB. try: ImageMode(pil_img.mode) except ValueError: print( 'warning: trying to convert image file to RGB from unexpected bitmode "%s"' % pil_img.mode) if self.black_to_transparent: # Avoid double-converting in the next filter. pil_img = pil_img.convert('RGBA') else: pil_img = pil_img.convert('RGB') # Convert pure black to transparent -- make sure to do this before any # colorspace processing. # # As far as I can tell, PIL has no good way to modify an image on a # pixel-by-pixel level in-place, which is really annoying. For now I'm # doing this processing in Numpy space, but for an image that almost # fills up memory we might have to use a different approach since this # one will involve holding two buffers at once. if self.black_to_transparent: if pil_img.mode != 'RGBA': pil_img = pil_img.convert('RGBA') a = np.asarray(pil_img) a = a.copy() # read-only buffer => writeable for i in range(a.shape[0]): nonblack = (a[i, ..., 0] > 0) np.logical_or(nonblack, a[i, ..., 1] > 0, out=nonblack) np.logical_or(nonblack, a[i, ..., 2] > 0, out=nonblack) a[i, ..., 3] *= nonblack # This is my attempt to preserve the image metadata and other # attributes, swapping out the pixel data only. There is probably # a better way to do this new_img = pil_image.fromarray(a, mode=pil_img.mode) pil_img.im = new_img.im del a, new_img # Make sure that we end up in the right color space. From experience, some # EPO images have funky colorspaces and we need to convert to sRGB to get # the tiled versions to appear correctly. if self.colorspace_processing != 'none' and 'icc_profile' in pil_img.info: assert self.colorspace_processing == 'srgb' # more modes, one day? try: from PIL import ImageCms # ImageCms doesn't raise import error if the implementation is unavailable # "for doc purposes". To see if it's available we need to actually try to # do something: out_prof = ImageCms.createProfile('sRGB') except ImportError: print( '''warning: colorspace processing requested, but no `ImageCms` module found in PIL. Your installation of PIL probably does not have colorspace support. Colors will not be transformed to sRGB and therefore may not appear as intended. Compare toasty's output to your source image and decide if this is acceptable to you. Consider a different setting of the `--colorspace-processing` argument to avoid this warning.''', file=sys.stderr) else: from io import BytesIO in_prof = ImageCms.getOpenProfile( BytesIO(pil_img.info['icc_profile'])) xform = ImageCms.buildTransform(in_prof, out_prof, pil_img.mode, pil_img.mode) ImageCms.applyTransform(pil_img, xform, inPlace=True) return Image.from_pil(pil_img)
def test_sanity(): # basic smoke test. # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings assert_equal(v[0], '0.1.0 pil') assert_equal(list(map(type, v)), [str, str, str, str]) # internal version number assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") i = ImageCms.profileToProfile(lena(), SRGB, SRGB) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") assert_equal(t.inputMode, "RGB") assert_equal(t.outputMode, "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) # get profile information for file assert_equal(ImageCms.getProfileName(SRGB).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), ['sRGB IEC61966-2.1', '', 'Copyright (c) 1998 Hewlett-Packard Company', '', 'WhitePoint : D65 (daylight)', '', 'Tests/icc/sRGB.icm']) assert_equal(ImageCms.getDefaultIntent(SRGB), 0) assert_equal(ImageCms.isIntentSupported( SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) # same, using profile object p = ImageCms.createProfile("sRGB") assert_equal(ImageCms.getProfileName(p).strip(), 'sRGB built-in - (lcms internal)') assert_equal(ImageCms.getProfileInfo(p).splitlines(), ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) assert_equal(ImageCms.getDefaultIntent(p), 0) assert_equal(ImageCms.isIntentSupported( p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) # extensions i = Image.open("Tests/images/rgb.jpg") p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) assert_equal(ImageCms.getProfileName(p).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') # the procedural pyCMS API uses PyCMSError for all sorts of errors assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) # test PointTransform convenience API im = lena().point(t) # try fetching the profile for the current display device assert_no_exception(lambda: ImageCms.get_display_profile())
result = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ outputMode = OUTMODE, inPlace = True) # now that the image is converted, save or display it if result == None: # this is the normal result when modifying in-place outputImage(im, "profileToProfile_inPlace") else: # something failed... print("profileToProfile in-place failed: %s" %result) print("profileToProfile in-place test completed successfully.") if TEST_buildTransform == True: # make a transform using the input and output profile path strings transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \ OUTMODE) # now, use the trnsform to convert a couple images im = Image.open(IMAGE) # transform im normally im2 = ImageCms.applyTransform(im, transform) outputImage(im2, "buildTransform") # then transform it again using the same transform, this time in-place. result = ImageCms.applyTransform(im, transform, inPlace = True) outputImage(im, "buildTransform_inPlace") print("buildTransform test completed successfully.") # and, to clean up a bit, delete the transform
def __init__(self, source): #Pass in the argument image location #get the location of the image self.source = source #determine if the image is local or hosted, then open it if 'http' in self.source: if(_verbose): print 'Reading from URL' file = cStringIO.StringIO(urllib.urlopen(self.source).read()) self.image = Image.open(file) else: try: self.image = Image.open(self.source) except IOError: print self.source print "Cannot load image. Be sure to include 'http://'' if loading from a website!" sys.exit() sRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(sRGB, pLab, "RGB", "LAB") self.labImage = ImageCms.applyTransform(self.image, t) self.imageWidth, self.imageHeight = self.image.size #Set width and height, which correspond to tuple values from self.image.size self.screenWidth, self.screenHeight = int(screen.split()[7]), int(screen.split()[9][:-1]) self.lost_res = 0.0 self.lost_aspect = 0.0 self.temp_rating = self.calcAvgImageTemp() self.final_rating = self.calcImageScore()