From 09b9198176a916741236833352e1279322b04054 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Feb 2020 20:57:27 +1100 Subject: [PATCH] Converted to pytest --- Tests/test_bmp_reference.py | 179 +++++---- Tests/test_file_blp.py | 29 +- Tests/test_file_ftex.py | 20 +- Tests/test_file_pcd.py | 23 +- Tests/test_file_ppm.py | 115 +++--- Tests/test_file_sgi.py | 140 +++---- Tests/test_file_tiff_metadata.py | 612 ++++++++++++++++--------------- Tests/test_format_hsv.py | 210 +++++------ Tests/test_image_convert.py | 419 ++++++++++----------- Tests/test_image_split.py | 105 +++--- Tests/test_imagepalette.py | 229 ++++++------ Tests/test_imagesequence.py | 157 ++++---- Tests/test_tiff_ifdrational.py | 76 ++-- Tests/test_uploader.py | 20 +- 14 files changed, 1190 insertions(+), 1144 deletions(-) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index c4fe25910..ade2901b7 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -3,109 +3,108 @@ import os import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_similar +from .helper import assert_image_similar base = os.path.join("Tests", "images", "bmp") -class TestBmpReference(PillowTestCase): - def get_files(self, d, ext=".bmp"): - return [ - os.path.join(base, d, f) - for f in os.listdir(os.path.join(base, d)) - if ext in f - ] +def get_files(d, ext=".bmp"): + return [ + os.path.join(base, d, f) for f in os.listdir(os.path.join(base, d)) if ext in f + ] - def test_bad(self): - """ These shouldn't crash/dos, but they shouldn't return anything - either """ - for f in self.get_files("b"): - def open(f): - try: - with Image.open(f) as im: - im.load() - except Exception: # as msg: - pass +def test_bad(): + """ These shouldn't crash/dos, but they shouldn't return anything + either """ + for f in get_files("b"): - # Assert that there is no unclosed file warning - pytest.warns(None, open, f) - - def test_questionable(self): - """ These shouldn't crash/dos, but it's not well defined that these - are in spec """ - supported = [ - "pal8os2v2.bmp", - "rgb24prof.bmp", - "pal1p1.bmp", - "pal8offs.bmp", - "rgb24lprof.bmp", - "rgb32fakealpha.bmp", - "rgb24largepal.bmp", - "pal8os2sp.bmp", - "rgb32bf-xbgr.bmp", - ] - for f in self.get_files("q"): + def open(f): try: with Image.open(f) as im: im.load() - if os.path.basename(f) not in supported: - print("Please add %s to the partially supported bmp specs." % f) except Exception: # as msg: - if os.path.basename(f) in supported: - raise + pass - def test_good(self): - """ These should all work. There's a set of target files in the - html directory that we can compare against. """ + # Assert that there is no unclosed file warning + pytest.warns(None, open, f) - # Target files, if they're not just replacing the extension - file_map = { - "pal1wb.bmp": "pal1.png", - "pal4rle.bmp": "pal4.png", - "pal8-0.bmp": "pal8.png", - "pal8rle.bmp": "pal8.png", - "pal8topdown.bmp": "pal8.png", - "pal8nonsquare.bmp": "pal8nonsquare-v.png", - "pal8os2.bmp": "pal8.png", - "pal8os2sp.bmp": "pal8.png", - "pal8os2v2.bmp": "pal8.png", - "pal8os2v2-16.bmp": "pal8.png", - "pal8v4.bmp": "pal8.png", - "pal8v5.bmp": "pal8.png", - "rgb16-565pal.bmp": "rgb16-565.png", - "rgb24pal.bmp": "rgb24.png", - "rgb32.bmp": "rgb24.png", - "rgb32bf.bmp": "rgb24.png", - } - def get_compare(f): - name = os.path.split(f)[1] - if name in file_map: - return os.path.join(base, "html", file_map[name]) - name = os.path.splitext(name)[0] - return os.path.join(base, "html", "%s.png" % name) +def test_questionable(): + """ These shouldn't crash/dos, but it's not well defined that these + are in spec """ + supported = [ + "pal8os2v2.bmp", + "rgb24prof.bmp", + "pal1p1.bmp", + "pal8offs.bmp", + "rgb24lprof.bmp", + "rgb32fakealpha.bmp", + "rgb24largepal.bmp", + "pal8os2sp.bmp", + "rgb32bf-xbgr.bmp", + ] + for f in get_files("q"): + try: + with Image.open(f) as im: + im.load() + if os.path.basename(f) not in supported: + print("Please add %s to the partially supported bmp specs." % f) + except Exception: # as msg: + if os.path.basename(f) in supported: + raise - for f in self.get_files("g"): - try: - with Image.open(f) as im: - im.load() - with Image.open(get_compare(f)) as compare: - compare.load() - if im.mode == "P": - # assert image similar doesn't really work - # with paletized image, since the palette might - # be differently ordered for an equivalent image. - im = im.convert("RGBA") - compare = im.convert("RGBA") - assert_image_similar(im, compare, 5) - except Exception as msg: - # there are three here that are unsupported: - unsupported = ( - os.path.join(base, "g", "rgb32bf.bmp"), - os.path.join(base, "g", "pal8rle.bmp"), - os.path.join(base, "g", "pal4rle.bmp"), - ) - if f not in unsupported: - self.fail("Unsupported Image {}: {}".format(f, msg)) +def test_good(): + """ These should all work. There's a set of target files in the + html directory that we can compare against. """ + + # Target files, if they're not just replacing the extension + file_map = { + "pal1wb.bmp": "pal1.png", + "pal4rle.bmp": "pal4.png", + "pal8-0.bmp": "pal8.png", + "pal8rle.bmp": "pal8.png", + "pal8topdown.bmp": "pal8.png", + "pal8nonsquare.bmp": "pal8nonsquare-v.png", + "pal8os2.bmp": "pal8.png", + "pal8os2sp.bmp": "pal8.png", + "pal8os2v2.bmp": "pal8.png", + "pal8os2v2-16.bmp": "pal8.png", + "pal8v4.bmp": "pal8.png", + "pal8v5.bmp": "pal8.png", + "rgb16-565pal.bmp": "rgb16-565.png", + "rgb24pal.bmp": "rgb24.png", + "rgb32.bmp": "rgb24.png", + "rgb32bf.bmp": "rgb24.png", + } + + def get_compare(f): + name = os.path.split(f)[1] + if name in file_map: + return os.path.join(base, "html", file_map[name]) + name = os.path.splitext(name)[0] + return os.path.join(base, "html", "%s.png" % name) + + for f in get_files("g"): + try: + with Image.open(f) as im: + im.load() + with Image.open(get_compare(f)) as compare: + compare.load() + if im.mode == "P": + # assert image similar doesn't really work + # with paletized image, since the palette might + # be differently ordered for an equivalent image. + im = im.convert("RGBA") + compare = im.convert("RGBA") + assert_image_similar(im, compare, 5) + + except Exception as msg: + # there are three here that are unsupported: + unsupported = ( + os.path.join(base, "g", "rgb32bf.bmp"), + os.path.join(base, "g", "pal8rle.bmp"), + os.path.join(base, "g", "pal4rle.bmp"), + ) + assert f in unsupported, "Unsupported Image {}: {}".format(f, msg) diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index d0bfe6eda..94c469c7f 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -1,20 +1,21 @@ from PIL import Image -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal -class TestFileBlp(PillowTestCase): - def test_load_blp2_raw(self): - with Image.open("Tests/images/blp/blp2_raw.blp") as im: - with Image.open("Tests/images/blp/blp2_raw.png") as target: - assert_image_equal(im, target) +def test_load_blp2_raw(): + with Image.open("Tests/images/blp/blp2_raw.blp") as im: + with Image.open("Tests/images/blp/blp2_raw.png") as target: + assert_image_equal(im, target) - def test_load_blp2_dxt1(self): - with Image.open("Tests/images/blp/blp2_dxt1.blp") as im: - with Image.open("Tests/images/blp/blp2_dxt1.png") as target: - assert_image_equal(im, target) - def test_load_blp2_dxt1a(self): - with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im: - with Image.open("Tests/images/blp/blp2_dxt1a.png") as target: - assert_image_equal(im, target) +def test_load_blp2_dxt1(): + with Image.open("Tests/images/blp/blp2_dxt1.blp") as im: + with Image.open("Tests/images/blp/blp2_dxt1.png") as target: + assert_image_equal(im, target) + + +def test_load_blp2_dxt1a(): + with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im: + with Image.open("Tests/images/blp/blp2_dxt1a.png") as target: + assert_image_equal(im, target) diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index 67ce8506f..9b4375cd4 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -1,15 +1,15 @@ from PIL import Image -from .helper import PillowTestCase, assert_image_equal, assert_image_similar +from .helper import assert_image_equal, assert_image_similar -class TestFileFtex(PillowTestCase): - def test_load_raw(self): - with Image.open("Tests/images/ftex_uncompressed.ftu") as im: - with Image.open("Tests/images/ftex_uncompressed.png") as target: - assert_image_equal(im, target) +def test_load_raw(): + with Image.open("Tests/images/ftex_uncompressed.ftu") as im: + with Image.open("Tests/images/ftex_uncompressed.png") as target: + assert_image_equal(im, target) - def test_load_dxt1(self): - with Image.open("Tests/images/ftex_dxt1.ftc") as im: - with Image.open("Tests/images/ftex_dxt1.png") as target: - assert_image_similar(im, target.convert("RGBA"), 15) + +def test_load_dxt1(): + with Image.open("Tests/images/ftex_dxt1.ftc") as im: + with Image.open("Tests/images/ftex_dxt1.png") as target: + assert_image_similar(im, target.convert("RGBA"), 15) diff --git a/Tests/test_file_pcd.py b/Tests/test_file_pcd.py index a59631e1a..dc45a48c1 100644 --- a/Tests/test_file_pcd.py +++ b/Tests/test_file_pcd.py @@ -1,18 +1,15 @@ from PIL import Image -from .helper import PillowTestCase +def test_load_raw(): + with Image.open("Tests/images/hopper.pcd") as im: + im.load() # should not segfault. -class TestFilePcd(PillowTestCase): - def test_load_raw(self): - with Image.open("Tests/images/hopper.pcd") as im: - im.load() # should not segfault. + # Note that this image was created with a resized hopper + # image, which was then converted to pcd with imagemagick + # and the colors are wonky in Pillow. It's unclear if this + # is a pillow or a convert issue, as other images not generated + # from convert look find on pillow and not imagemagick. - # Note that this image was created with a resized hopper - # image, which was then converted to pcd with imagemagick - # and the colors are wonky in Pillow. It's unclear if this - # is a pillow or a convert issue, as other images not generated - # from convert look find on pillow and not imagemagick. - - # target = hopper().resize((768,512)) - # assert_image_similar(im, target, 10) + # target = hopper().resize((768,512)) + # assert_image_similar(im, target, 10) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index dc8daa98f..6b91ba28a 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -1,77 +1,82 @@ import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper # sample ppm stream -test_file = "Tests/images/hopper.ppm" +TEST_FILE = "Tests/images/hopper.ppm" -class TestFilePpm(PillowTestCase): - def test_sanity(self): - with Image.open(test_file) as im: - im.load() - assert im.mode == "RGB" - assert im.size == (128, 128) - assert im.format, "PPM" - assert im.get_format_mimetype() == "image/x-portable-pixmap" +def test_sanity(): + with Image.open(TEST_FILE) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format, "PPM" + assert im.get_format_mimetype() == "image/x-portable-pixmap" - def test_16bit_pgm(self): - with Image.open("Tests/images/16_bit_binary.pgm") as im: - im.load() - assert im.mode == "I" - assert im.size == (20, 100) - assert im.get_format_mimetype() == "image/x-portable-graymap" - with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt: - assert_image_equal(im, tgt) +def test_16bit_pgm(): + with Image.open("Tests/images/16_bit_binary.pgm") as im: + im.load() + assert im.mode == "I" + assert im.size == (20, 100) + assert im.get_format_mimetype() == "image/x-portable-graymap" - def test_16bit_pgm_write(self): - with Image.open("Tests/images/16_bit_binary.pgm") as im: - im.load() + with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt: + assert_image_equal(im, tgt) - f = self.tempfile("temp.pgm") - im.save(f, "PPM") - with Image.open(f) as reloaded: - assert_image_equal(im, reloaded) +def test_16bit_pgm_write(tmp_path): + with Image.open("Tests/images/16_bit_binary.pgm") as im: + im.load() - def test_pnm(self): - with Image.open("Tests/images/hopper.pnm") as im: - assert_image_similar(im, hopper(), 0.0001) + f = str(tmp_path / "temp.pgm") + im.save(f, "PPM") - f = self.tempfile("temp.pnm") - im.save(f) + with Image.open(f) as reloaded: + assert_image_equal(im, reloaded) - with Image.open(f) as reloaded: - assert_image_equal(im, reloaded) - def test_truncated_file(self): - path = self.tempfile("temp.pgm") - with open(path, "w") as f: - f.write("P6") +def test_pnm(tmp_path): + with Image.open("Tests/images/hopper.pnm") as im: + assert_image_similar(im, hopper(), 0.0001) - with pytest.raises(ValueError): - Image.open(path) + f = str(tmp_path / "temp.pnm") + im.save(f) - def test_neg_ppm(self): - # Storage.c accepted negative values for xsize, ysize. the - # internal open_ppm function didn't check for sanity but it - # has been removed. The default opener doesn't accept negative - # sizes. + with Image.open(f) as reloaded: + assert_image_equal(im, reloaded) - with pytest.raises(IOError): - Image.open("Tests/images/negative_size.ppm") - def test_mimetypes(self): - path = self.tempfile("temp.pgm") +def test_truncated_file(tmp_path): + path = str(tmp_path / "temp.pgm") + with open(path, "w") as f: + f.write("P6") - with open(path, "w") as f: - f.write("P4\n128 128\n255") - with Image.open(path) as im: - assert im.get_format_mimetype() == "image/x-portable-bitmap" + with pytest.raises(ValueError): + Image.open(path) - with open(path, "w") as f: - f.write("PyCMYK\n128 128\n255") - with Image.open(path) as im: - assert im.get_format_mimetype() == "image/x-portable-anymap" + +def test_neg_ppm(): + # Storage.c accepted negative values for xsize, ysize. the + # internal open_ppm function didn't check for sanity but it + # has been removed. The default opener doesn't accept negative + # sizes. + + with pytest.raises(IOError): + Image.open("Tests/images/negative_size.ppm") + + +def test_mimetypes(tmp_path): + path = str(tmp_path / "temp.pgm") + + with open(path, "w") as f: + f.write("P4\n128 128\n255") + with Image.open(path) as im: + assert im.get_format_mimetype() == "image/x-portable-bitmap" + + with open(path, "w") as f: + f.write("PyCMYK\n128 128\n255") + with Image.open(path) as im: + assert im.get_format_mimetype() == "image/x-portable-anymap" diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 4a606548c..cb16276ce 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -1,92 +1,100 @@ import pytest from PIL import Image, SgiImagePlugin -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper -class TestFileSgi(PillowTestCase): - def test_rgb(self): - # Created with ImageMagick then renamed: - # convert hopper.ppm -compress None sgi:hopper.rgb - test_file = "Tests/images/hopper.rgb" +def test_rgb(): + # Created with ImageMagick then renamed: + # convert hopper.ppm -compress None sgi:hopper.rgb + test_file = "Tests/images/hopper.rgb" - with Image.open(test_file) as im: - assert_image_equal(im, hopper()) - assert im.get_format_mimetype() == "image/rgb" + with Image.open(test_file) as im: + assert_image_equal(im, hopper()) + assert im.get_format_mimetype() == "image/rgb" - def test_rgb16(self): - test_file = "Tests/images/hopper16.rgb" - with Image.open(test_file) as im: - assert_image_equal(im, hopper()) +def test_rgb16(): + test_file = "Tests/images/hopper16.rgb" - def test_l(self): - # Created with ImageMagick - # convert hopper.ppm -monochrome -compress None sgi:hopper.bw - test_file = "Tests/images/hopper.bw" + with Image.open(test_file) as im: + assert_image_equal(im, hopper()) - with Image.open(test_file) as im: - assert_image_similar(im, hopper("L"), 2) - assert im.get_format_mimetype() == "image/sgi" - def test_rgba(self): - # Created with ImageMagick: - # convert transparent.png -compress None transparent.sgi - test_file = "Tests/images/transparent.sgi" +def test_l(): + # Created with ImageMagick + # convert hopper.ppm -monochrome -compress None sgi:hopper.bw + test_file = "Tests/images/hopper.bw" - with Image.open(test_file) as im: - with Image.open("Tests/images/transparent.png") as target: - assert_image_equal(im, target) - assert im.get_format_mimetype() == "image/sgi" + with Image.open(test_file) as im: + assert_image_similar(im, hopper("L"), 2) + assert im.get_format_mimetype() == "image/sgi" - def test_rle(self): - # Created with ImageMagick: - # convert hopper.ppm hopper.sgi - test_file = "Tests/images/hopper.sgi" - with Image.open(test_file) as im: - with Image.open("Tests/images/hopper.rgb") as target: - assert_image_equal(im, target) +def test_rgba(): + # Created with ImageMagick: + # convert transparent.png -compress None transparent.sgi + test_file = "Tests/images/transparent.sgi" - def test_rle16(self): - test_file = "Tests/images/tv16.sgi" + with Image.open(test_file) as im: + with Image.open("Tests/images/transparent.png") as target: + assert_image_equal(im, target) + assert im.get_format_mimetype() == "image/sgi" - with Image.open(test_file) as im: - with Image.open("Tests/images/tv.rgb") as target: - assert_image_equal(im, target) - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" +def test_rle(): + # Created with ImageMagick: + # convert hopper.ppm hopper.sgi + test_file = "Tests/images/hopper.sgi" - with pytest.raises(ValueError): - SgiImagePlugin.SgiImageFile(invalid_file) + with Image.open(test_file) as im: + with Image.open("Tests/images/hopper.rgb") as target: + assert_image_equal(im, target) - def test_write(self): - def roundtrip(img): - out = self.tempfile("temp.sgi") - img.save(out, format="sgi") - with Image.open(out) as reloaded: - assert_image_equal(img, reloaded) - for mode in ("L", "RGB", "RGBA"): - roundtrip(hopper(mode)) +def test_rle16(): + test_file = "Tests/images/tv16.sgi" - # Test 1 dimension for an L mode image - roundtrip(Image.new("L", (10, 1))) + with Image.open(test_file) as im: + with Image.open("Tests/images/tv.rgb") as target: + assert_image_equal(im, target) - def test_write16(self): - test_file = "Tests/images/hopper16.rgb" - with Image.open(test_file) as im: - out = self.tempfile("temp.sgi") - im.save(out, format="sgi", bpc=2) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - with Image.open(out) as reloaded: - assert_image_equal(im, reloaded) + with pytest.raises(ValueError): + SgiImagePlugin.SgiImageFile(invalid_file) - def test_unsupported_mode(self): - im = hopper("LA") - out = self.tempfile("temp.sgi") - with pytest.raises(ValueError): - im.save(out, format="sgi") +def test_write(tmp_path): + def roundtrip(img): + out = str(tmp_path / "temp.sgi") + img.save(out, format="sgi") + with Image.open(out) as reloaded: + assert_image_equal(img, reloaded) + + for mode in ("L", "RGB", "RGBA"): + roundtrip(hopper(mode)) + + # Test 1 dimension for an L mode image + roundtrip(Image.new("L", (10, 1))) + + +def test_write16(tmp_path): + test_file = "Tests/images/hopper16.rgb" + + with Image.open(test_file) as im: + out = str(tmp_path / "temp.sgi") + im.save(out, format="sgi", bpc=2) + + with Image.open(out) as reloaded: + assert_image_equal(im, reloaded) + + +def test_unsupported_mode(tmp_path): + im = hopper("LA") + out = str(tmp_path / "temp.sgi") + + with pytest.raises(ValueError): + im.save(out, format="sgi") diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 5554a25e9..9fe601bd6 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -5,321 +5,335 @@ import pytest from PIL import Image, TiffImagePlugin, TiffTags from PIL.TiffImagePlugin import IFDRational -from .helper import PillowTestCase, assert_deep_equal, hopper - -tag_ids = {info.name: info.value for info in TiffTags.TAGS_V2.values()} - - -class TestFileTiffMetadata(PillowTestCase): - def test_rt_metadata(self): - """ Test writing arbitrary metadata into the tiff image directory - Use case is ImageJ private tags, one numeric, one arbitrary - data. https://github.com/python-pillow/Pillow/issues/291 - """ - - img = hopper() - - # Behaviour change: re #1416 - # Pre ifd rewrite, ImageJMetaData was being written as a string(2), - # Post ifd rewrite, it's defined as arbitrary bytes(7). It should - # roundtrip with the actual bytes, rather than stripped text - # of the premerge tests. - # - # For text items, we still have to decode('ascii','replace') because - # the tiff file format can't take 8 bit bytes in that field. - - basetextdata = "This is some arbitrary metadata for a text field" - bindata = basetextdata.encode("ascii") + b" \xff" - textdata = basetextdata + " " + chr(255) - reloaded_textdata = basetextdata + " ?" - floatdata = 12.345 - doubledata = 67.89 - info = TiffImagePlugin.ImageFileDirectory() - - ImageJMetaData = tag_ids["ImageJMetaData"] - ImageJMetaDataByteCounts = tag_ids["ImageJMetaDataByteCounts"] - ImageDescription = tag_ids["ImageDescription"] - - info[ImageJMetaDataByteCounts] = len(bindata) - info[ImageJMetaData] = bindata - info[tag_ids["RollAngle"]] = floatdata - info.tagtype[tag_ids["RollAngle"]] = 11 - info[tag_ids["YawAngle"]] = doubledata - info.tagtype[tag_ids["YawAngle"]] = 12 - - info[ImageDescription] = textdata - - f = self.tempfile("temp.tif") - - img.save(f, tiffinfo=info) - - with Image.open(f) as loaded: - - assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata),) - assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata),) - - assert loaded.tag[ImageJMetaData] == bindata - assert loaded.tag_v2[ImageJMetaData] == bindata - - assert loaded.tag[ImageDescription] == (reloaded_textdata,) - assert loaded.tag_v2[ImageDescription] == reloaded_textdata - - loaded_float = loaded.tag[tag_ids["RollAngle"]][0] - assert round(abs(loaded_float - floatdata), 5) == 0 - loaded_double = loaded.tag[tag_ids["YawAngle"]][0] - assert round(abs(loaded_double - doubledata), 7) == 0 - - # check with 2 element ImageJMetaDataByteCounts, issue #2006 - - info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8) - img.save(f, tiffinfo=info) - with Image.open(f) as loaded: - - assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) - assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) - - def test_read_metadata(self): - with Image.open("Tests/images/hopper_g4.tif") as img: - - assert { - "YResolution": IFDRational(4294967295, 113653537), - "PlanarConfiguration": 1, - "BitsPerSample": (1,), - "ImageLength": 128, - "Compression": 4, - "FillOrder": 1, - "RowsPerStrip": 128, - "ResolutionUnit": 3, - "PhotometricInterpretation": 0, - "PageNumber": (0, 1), - "XResolution": IFDRational(4294967295, 113653537), - "ImageWidth": 128, - "Orientation": 1, - "StripByteCounts": (1968,), - "SamplesPerPixel": 1, - "StripOffsets": (8,), - } == img.tag_v2.named() - - assert { - "YResolution": ((4294967295, 113653537),), - "PlanarConfiguration": (1,), - "BitsPerSample": (1,), - "ImageLength": (128,), - "Compression": (4,), - "FillOrder": (1,), - "RowsPerStrip": (128,), - "ResolutionUnit": (3,), - "PhotometricInterpretation": (0,), - "PageNumber": (0, 1), - "XResolution": ((4294967295, 113653537),), - "ImageWidth": (128,), - "Orientation": (1,), - "StripByteCounts": (1968,), - "SamplesPerPixel": (1,), - "StripOffsets": (8,), - } == img.tag.named() - - def test_write_metadata(self): - """ Test metadata writing through the python code """ - with Image.open("Tests/images/hopper.tif") as img: - f = self.tempfile("temp.tiff") - img.save(f, tiffinfo=img.tag) - - original = img.tag_v2.named() - - with Image.open(f) as loaded: - reloaded = loaded.tag_v2.named() - - ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"] - - for tag, value in reloaded.items(): - if tag in ignored: - continue - if isinstance(original[tag], tuple) and isinstance( - original[tag][0], IFDRational - ): - # Need to compare element by element in the tuple, - # not comparing tuples of object references - assert_deep_equal( - original[tag], - value, - "{} didn't roundtrip, {}, {}".format(tag, original[tag], value), - ) - else: - assert original[tag] == value, "{} didn't roundtrip, {}, {}".format( - tag, original[tag], value - ) - - for tag, value in original.items(): - if tag not in ignored: - assert value == reloaded[tag], "%s didn't roundtrip" % tag - - def test_no_duplicate_50741_tag(self): - assert tag_ids["MakerNoteSafety"] == 50741 - assert tag_ids["BestQualityScale"] == 50780 - - def test_empty_metadata(self): - f = io.BytesIO(b"II*\x00\x08\x00\x00\x00") - head = f.read(8) - info = TiffImagePlugin.ImageFileDirectory(head) - # Should not raise struct.error. - pytest.warns(UserWarning, info.load, f) - - def test_iccprofile(self): - # https://github.com/python-pillow/Pillow/issues/1462 - out = self.tempfile("temp.tiff") - with Image.open("Tests/images/hopper.iccprofile.tif") as im: - im.save(out) - - with Image.open(out) as reloaded: - assert not isinstance(im.info["icc_profile"], tuple) - assert im.info["icc_profile"] == reloaded.info["icc_profile"] - - def test_iccprofile_binary(self): - # https://github.com/python-pillow/Pillow/issues/1526 - # We should be able to load this, - # but probably won't be able to save it. - - with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im: - assert im.tag_v2.tagtype[34675] == 1 - assert im.info["icc_profile"] - - def test_iccprofile_save_png(self): - with Image.open("Tests/images/hopper.iccprofile.tif") as im: - outfile = self.tempfile("temp.png") - im.save(outfile) - - def test_iccprofile_binary_save_png(self): - with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im: - outfile = self.tempfile("temp.png") - im.save(outfile) - - def test_exif_div_zero(self): - im = hopper() - info = TiffImagePlugin.ImageFileDirectory_v2() - info[41988] = TiffImagePlugin.IFDRational(0, 0) - - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") - - with Image.open(out) as reloaded: - assert 0 == reloaded.tag_v2[41988].numerator - assert 0 == reloaded.tag_v2[41988].denominator - - def test_ifd_unsigned_rational(self): - im = hopper() - info = TiffImagePlugin.ImageFileDirectory_v2() - - max_long = 2 ** 32 - 1 - - # 4 bytes unsigned long - numerator = max_long - - info[41493] = TiffImagePlugin.IFDRational(numerator, 1) - - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") - - with Image.open(out) as reloaded: - assert max_long == reloaded.tag_v2[41493].numerator - assert 1 == reloaded.tag_v2[41493].denominator - - # out of bounds of 4 byte unsigned long - numerator = max_long + 1 - - info[41493] = TiffImagePlugin.IFDRational(numerator, 1) - - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") - - with Image.open(out) as reloaded: - assert max_long == reloaded.tag_v2[41493].numerator - assert 1 == reloaded.tag_v2[41493].denominator - - def test_ifd_signed_rational(self): - im = hopper() - info = TiffImagePlugin.ImageFileDirectory_v2() - - # pair of 4 byte signed longs - numerator = 2 ** 31 - 1 - denominator = -(2 ** 31) - - info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) +from .helper import assert_deep_equal, hopper + +TAG_IDS = {info.name: info.value for info in TiffTags.TAGS_V2.values()} + + +def test_rt_metadata(tmp_path): + """ Test writing arbitrary metadata into the tiff image directory + Use case is ImageJ private tags, one numeric, one arbitrary + data. https://github.com/python-pillow/Pillow/issues/291 + """ + + img = hopper() + + # Behaviour change: re #1416 + # Pre ifd rewrite, ImageJMetaData was being written as a string(2), + # Post ifd rewrite, it's defined as arbitrary bytes(7). It should + # roundtrip with the actual bytes, rather than stripped text + # of the premerge tests. + # + # For text items, we still have to decode('ascii','replace') because + # the tiff file format can't take 8 bit bytes in that field. + + basetextdata = "This is some arbitrary metadata for a text field" + bindata = basetextdata.encode("ascii") + b" \xff" + textdata = basetextdata + " " + chr(255) + reloaded_textdata = basetextdata + " ?" + floatdata = 12.345 + doubledata = 67.89 + info = TiffImagePlugin.ImageFileDirectory() + + ImageJMetaData = TAG_IDS["ImageJMetaData"] + ImageJMetaDataByteCounts = TAG_IDS["ImageJMetaDataByteCounts"] + ImageDescription = TAG_IDS["ImageDescription"] + + info[ImageJMetaDataByteCounts] = len(bindata) + info[ImageJMetaData] = bindata + info[TAG_IDS["RollAngle"]] = floatdata + info.tagtype[TAG_IDS["RollAngle"]] = 11 + info[TAG_IDS["YawAngle"]] = doubledata + info.tagtype[TAG_IDS["YawAngle"]] = 12 + + info[ImageDescription] = textdata + + f = str(tmp_path / "temp.tif") + + img.save(f, tiffinfo=info) + + with Image.open(f) as loaded: + + assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata),) + assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata),) + + assert loaded.tag[ImageJMetaData] == bindata + assert loaded.tag_v2[ImageJMetaData] == bindata + + assert loaded.tag[ImageDescription] == (reloaded_textdata,) + assert loaded.tag_v2[ImageDescription] == reloaded_textdata + + loaded_float = loaded.tag[TAG_IDS["RollAngle"]][0] + assert round(abs(loaded_float - floatdata), 5) == 0 + loaded_double = loaded.tag[TAG_IDS["YawAngle"]][0] + assert round(abs(loaded_double - doubledata), 7) == 0 + + # check with 2 element ImageJMetaDataByteCounts, issue #2006 + + info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8) + img.save(f, tiffinfo=info) + with Image.open(f) as loaded: + + assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) + assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) + + +def test_read_metadata(): + with Image.open("Tests/images/hopper_g4.tif") as img: + + assert { + "YResolution": IFDRational(4294967295, 113653537), + "PlanarConfiguration": 1, + "BitsPerSample": (1,), + "ImageLength": 128, + "Compression": 4, + "FillOrder": 1, + "RowsPerStrip": 128, + "ResolutionUnit": 3, + "PhotometricInterpretation": 0, + "PageNumber": (0, 1), + "XResolution": IFDRational(4294967295, 113653537), + "ImageWidth": 128, + "Orientation": 1, + "StripByteCounts": (1968,), + "SamplesPerPixel": 1, + "StripOffsets": (8,), + } == img.tag_v2.named() + + assert { + "YResolution": ((4294967295, 113653537),), + "PlanarConfiguration": (1,), + "BitsPerSample": (1,), + "ImageLength": (128,), + "Compression": (4,), + "FillOrder": (1,), + "RowsPerStrip": (128,), + "ResolutionUnit": (3,), + "PhotometricInterpretation": (0,), + "PageNumber": (0, 1), + "XResolution": ((4294967295, 113653537),), + "ImageWidth": (128,), + "Orientation": (1,), + "StripByteCounts": (1968,), + "SamplesPerPixel": (1,), + "StripOffsets": (8,), + } == img.tag.named() + + +def test_write_metadata(tmp_path): + """ Test metadata writing through the python code """ + with Image.open("Tests/images/hopper.tif") as img: + f = str(tmp_path / "temp.tiff") + img.save(f, tiffinfo=img.tag) + + original = img.tag_v2.named() + + with Image.open(f) as loaded: + reloaded = loaded.tag_v2.named() + + ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"] - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") + for tag, value in reloaded.items(): + if tag in ignored: + continue + if isinstance(original[tag], tuple) and isinstance( + original[tag][0], IFDRational + ): + # Need to compare element by element in the tuple, + # not comparing tuples of object references + assert_deep_equal( + original[tag], + value, + "{} didn't roundtrip, {}, {}".format(tag, original[tag], value), + ) + else: + assert original[tag] == value, "{} didn't roundtrip, {}, {}".format( + tag, original[tag], value + ) - with Image.open(out) as reloaded: - assert numerator == reloaded.tag_v2[37380].numerator - assert denominator == reloaded.tag_v2[37380].denominator + for tag, value in original.items(): + if tag not in ignored: + assert value == reloaded[tag], "%s didn't roundtrip" % tag - numerator = -(2 ** 31) - denominator = 2 ** 31 - 1 - info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) +def test_no_duplicate_50741_tag(): + assert TAG_IDS["MakerNoteSafety"] == 50741 + assert TAG_IDS["BestQualityScale"] == 50780 - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") - with Image.open(out) as reloaded: - assert numerator == reloaded.tag_v2[37380].numerator - assert denominator == reloaded.tag_v2[37380].denominator +def test_empty_metadata(): + f = io.BytesIO(b"II*\x00\x08\x00\x00\x00") + head = f.read(8) + info = TiffImagePlugin.ImageFileDirectory(head) + # Should not raise struct.error. + pytest.warns(UserWarning, info.load, f) - # out of bounds of 4 byte signed long - numerator = -(2 ** 31) - 1 - denominator = 1 - info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) +def test_iccprofile(tmp_path): + # https://github.com/python-pillow/Pillow/issues/1462 + out = str(tmp_path / "temp.tiff") + with Image.open("Tests/images/hopper.iccprofile.tif") as im: + im.save(out) - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") + with Image.open(out) as reloaded: + assert not isinstance(im.info["icc_profile"], tuple) + assert im.info["icc_profile"] == reloaded.info["icc_profile"] - with Image.open(out) as reloaded: - assert 2 ** 31 - 1 == reloaded.tag_v2[37380].numerator - assert -1 == reloaded.tag_v2[37380].denominator - def test_ifd_signed_long(self): - im = hopper() - info = TiffImagePlugin.ImageFileDirectory_v2() +def test_iccprofile_binary(): + # https://github.com/python-pillow/Pillow/issues/1526 + # We should be able to load this, + # but probably won't be able to save it. - info[37000] = -60000 + with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im: + assert im.tag_v2.tagtype[34675] == 1 + assert im.info["icc_profile"] - out = self.tempfile("temp.tiff") - im.save(out, tiffinfo=info, compression="raw") - with Image.open(out) as reloaded: - assert reloaded.tag_v2[37000] == -60000 +def test_iccprofile_save_png(tmp_path): + with Image.open("Tests/images/hopper.iccprofile.tif") as im: + outfile = str(tmp_path / "temp.png") + im.save(outfile) - def test_empty_values(self): - data = io.BytesIO( - b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a " - b"text\x00\x00" - ) - head = data.read(8) - info = TiffImagePlugin.ImageFileDirectory_v2(head) - info.load(data) - # Should not raise ValueError. - info = dict(info) - assert 33432 in info - def test_PhotoshopInfo(self): - with Image.open("Tests/images/issue_2278.tif") as im: - assert len(im.tag_v2[34377]) == 1 - assert isinstance(im.tag_v2[34377][0], bytes) - out = self.tempfile("temp.tiff") - im.save(out) - with Image.open(out) as reloaded: - assert len(reloaded.tag_v2[34377]) == 1 - assert isinstance(reloaded.tag_v2[34377][0], bytes) +def test_iccprofile_binary_save_png(tmp_path): + with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im: + outfile = str(tmp_path / "temp.png") + im.save(outfile) - def test_too_many_entries(self): - ifd = TiffImagePlugin.ImageFileDirectory_v2() - # 277: ("SamplesPerPixel", SHORT, 1), - ifd._tagdata[277] = struct.pack("hh", 4, 4) - ifd.tagtype[277] = TiffTags.SHORT +def test_exif_div_zero(tmp_path): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + info[41988] = TiffImagePlugin.IFDRational(0, 0) - # Should not raise ValueError. - pytest.warns(UserWarning, lambda: ifd[277]) + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert 0 == reloaded.tag_v2[41988].numerator + assert 0 == reloaded.tag_v2[41988].denominator + + +def test_ifd_unsigned_rational(tmp_path): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + + max_long = 2 ** 32 - 1 + + # 4 bytes unsigned long + numerator = max_long + + info[41493] = TiffImagePlugin.IFDRational(numerator, 1) + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert max_long == reloaded.tag_v2[41493].numerator + assert 1 == reloaded.tag_v2[41493].denominator + + # out of bounds of 4 byte unsigned long + numerator = max_long + 1 + + info[41493] = TiffImagePlugin.IFDRational(numerator, 1) + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert max_long == reloaded.tag_v2[41493].numerator + assert 1 == reloaded.tag_v2[41493].denominator + + +def test_ifd_signed_rational(tmp_path): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + + # pair of 4 byte signed longs + numerator = 2 ** 31 - 1 + denominator = -(2 ** 31) + + info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert numerator == reloaded.tag_v2[37380].numerator + assert denominator == reloaded.tag_v2[37380].denominator + + numerator = -(2 ** 31) + denominator = 2 ** 31 - 1 + + info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert numerator == reloaded.tag_v2[37380].numerator + assert denominator == reloaded.tag_v2[37380].denominator + + # out of bounds of 4 byte signed long + numerator = -(2 ** 31) - 1 + denominator = 1 + + info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert 2 ** 31 - 1 == reloaded.tag_v2[37380].numerator + assert -1 == reloaded.tag_v2[37380].denominator + + +def test_ifd_signed_long(tmp_path): + im = hopper() + info = TiffImagePlugin.ImageFileDirectory_v2() + + info[37000] = -60000 + + out = str(tmp_path / "temp.tiff") + im.save(out, tiffinfo=info, compression="raw") + + with Image.open(out) as reloaded: + assert reloaded.tag_v2[37000] == -60000 + + +def test_empty_values(): + data = io.BytesIO( + b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a " + b"text\x00\x00" + ) + head = data.read(8) + info = TiffImagePlugin.ImageFileDirectory_v2(head) + info.load(data) + # Should not raise ValueError. + info = dict(info) + assert 33432 in info + + +def test_PhotoshopInfo(tmp_path): + with Image.open("Tests/images/issue_2278.tif") as im: + assert len(im.tag_v2[34377]) == 1 + assert isinstance(im.tag_v2[34377][0], bytes) + out = str(tmp_path / "temp.tiff") + im.save(out) + with Image.open(out) as reloaded: + assert len(reloaded.tag_v2[34377]) == 1 + assert isinstance(reloaded.tag_v2[34377][0], bytes) + + +def test_too_many_entries(): + ifd = TiffImagePlugin.ImageFileDirectory_v2() + + # 277: ("SamplesPerPixel", SHORT, 1), + ifd._tagdata[277] = struct.pack("hh", 4, 4) + ifd.tagtype[277] = TiffTags.SHORT + + # Should not raise ValueError. + pytest.warns(UserWarning, lambda: ifd[277]) diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index b940b823d..d10b1acfd 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -3,142 +3,134 @@ import itertools from PIL import Image -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import assert_image_similar, hopper -class TestFormatHSV(PillowTestCase): - def int_to_float(self, i): - return i / 255 +def int_to_float(i): + return i / 255 - def str_to_float(self, i): - return ord(i) / 255 - def tuple_to_ints(self, tp): - x, y, z = tp - return int(x * 255.0), int(y * 255.0), int(z * 255.0) +def str_to_float(i): + return ord(i) / 255 - def test_sanity(self): - Image.new("HSV", (100, 100)) - def wedge(self): - w = Image._wedge() - w90 = w.rotate(90) +def tuple_to_ints(tp): + x, y, z = tp + return int(x * 255.0), int(y * 255.0), int(z * 255.0) - (px, h) = w.size - r = Image.new("L", (px * 3, h)) - g = r.copy() - b = r.copy() +def test_sanity(): + Image.new("HSV", (100, 100)) - r.paste(w, (0, 0)) - r.paste(w90, (px, 0)) - g.paste(w90, (0, 0)) - g.paste(w, (2 * px, 0)) +def wedge(): + w = Image._wedge() + w90 = w.rotate(90) - b.paste(w, (px, 0)) - b.paste(w90, (2 * px, 0)) + (px, h) = w.size - img = Image.merge("RGB", (r, g, b)) + r = Image.new("L", (px * 3, h)) + g = r.copy() + b = r.copy() - return img + r.paste(w, (0, 0)) + r.paste(w90, (px, 0)) - def to_xxx_colorsys(self, im, func, mode): - # convert the hard way using the library colorsys routines. + g.paste(w90, (0, 0)) + g.paste(w, (2 * px, 0)) - (r, g, b) = im.split() + b.paste(w, (px, 0)) + b.paste(w90, (2 * px, 0)) - conv_func = self.int_to_float + img = Image.merge("RGB", (r, g, b)) - converted = [ - self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b))) - for (_r, _g, _b) in itertools.zip_longest( - r.tobytes(), g.tobytes(), b.tobytes() - ) - ] + return img - new_bytes = b"".join( - bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted - ) - hsv = Image.frombytes(mode, r.size, new_bytes) +def to_xxx_colorsys(im, func, mode): + # convert the hard way using the library colorsys routines. - return hsv + (r, g, b) = im.split() - def to_hsv_colorsys(self, im): - return self.to_xxx_colorsys(im, colorsys.rgb_to_hsv, "HSV") + conv_func = int_to_float - def to_rgb_colorsys(self, im): - return self.to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB") + converted = [ + tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b))) + for (_r, _g, _b) in itertools.zip_longest(r.tobytes(), g.tobytes(), b.tobytes()) + ] - def test_wedge(self): - src = self.wedge().resize((3 * 32, 32), Image.BILINEAR) - im = src.convert("HSV") - comparable = self.to_hsv_colorsys(src) + new_bytes = b"".join( + bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted + ) - assert_image_similar( - im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" - ) - assert_image_similar( - im.getchannel(1), - comparable.getchannel(1), - 1, - "Saturation conversion is wrong", - ) - assert_image_similar( - im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" - ) + hsv = Image.frombytes(mode, r.size, new_bytes) - comparable = src - im = im.convert("RGB") + return hsv - assert_image_similar( - im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong" - ) - assert_image_similar( - im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong" - ) - assert_image_similar( - im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong" - ) - def test_convert(self): - im = hopper("RGB").convert("HSV") - comparable = self.to_hsv_colorsys(hopper("RGB")) +def to_hsv_colorsys(im): + return to_xxx_colorsys(im, colorsys.rgb_to_hsv, "HSV") - assert_image_similar( - im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" - ) - assert_image_similar( - im.getchannel(1), - comparable.getchannel(1), - 1, - "Saturation conversion is wrong", - ) - assert_image_similar( - im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" - ) - def test_hsv_to_rgb(self): - comparable = self.to_hsv_colorsys(hopper("RGB")) - converted = comparable.convert("RGB") - comparable = self.to_rgb_colorsys(comparable) +def to_rgb_colorsys(im): + return to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB") - assert_image_similar( - converted.getchannel(0), - comparable.getchannel(0), - 3, - "R conversion is wrong", - ) - assert_image_similar( - converted.getchannel(1), - comparable.getchannel(1), - 3, - "G conversion is wrong", - ) - assert_image_similar( - converted.getchannel(2), - comparable.getchannel(2), - 3, - "B conversion is wrong", - ) + +def test_wedge(): + src = wedge().resize((3 * 32, 32), Image.BILINEAR) + im = src.convert("HSV") + comparable = to_hsv_colorsys(src) + + assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" + ) + assert_image_similar( + im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong", + ) + assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" + ) + + comparable = src + im = im.convert("RGB") + + assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong" + ) + assert_image_similar( + im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong" + ) + assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong" + ) + + +def test_convert(): + im = hopper("RGB").convert("HSV") + comparable = to_hsv_colorsys(hopper("RGB")) + + assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" + ) + assert_image_similar( + im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong", + ) + assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" + ) + + +def test_hsv_to_rgb(): + comparable = to_hsv_colorsys(hopper("RGB")) + converted = comparable.convert("RGB") + comparable = to_rgb_colorsys(comparable) + + assert_image_similar( + converted.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong", + ) + assert_image_similar( + converted.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong", + ) + assert_image_similar( + converted.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong", + ) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index e0b9570e9..cf83922b6 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -1,250 +1,261 @@ import pytest from PIL import Image -from .helper import ( - PillowTestCase, - assert_image, - assert_image_equal, - assert_image_similar, - hopper, -) +from .helper import assert_image, assert_image_equal, assert_image_similar, hopper -class TestImageConvert(PillowTestCase): - def test_sanity(self): - def convert(im, mode): - out = im.convert(mode) - assert out.mode == mode - assert out.size == im.size +def test_sanity(): + def convert(im, mode): + out = im.convert(mode) + assert out.mode == mode + assert out.size == im.size - modes = ( - "1", - "L", - "LA", - "P", - "PA", - "I", - "F", - "RGB", - "RGBA", - "RGBX", - "CMYK", - "YCbCr", - "HSV", - ) + modes = ( + "1", + "L", + "LA", + "P", + "PA", + "I", + "F", + "RGB", + "RGBA", + "RGBX", + "CMYK", + "YCbCr", + "HSV", + ) + for mode in modes: + im = hopper(mode) for mode in modes: - im = hopper(mode) - for mode in modes: - convert(im, mode) + convert(im, mode) - # Check 0 - im = Image.new(mode, (0, 0)) - for mode in modes: - convert(im, mode) + # Check 0 + im = Image.new(mode, (0, 0)) + for mode in modes: + convert(im, mode) - def test_default(self): - im = hopper("P") - assert_image(im, "P", im.size) - im = im.convert() - assert_image(im, "RGB", im.size) - im = im.convert() - assert_image(im, "RGB", im.size) +def test_default(): - # ref https://github.com/python-pillow/Pillow/issues/274 + im = hopper("P") + assert_image(im, "P", im.size) + im = im.convert() + assert_image(im, "RGB", im.size) + im = im.convert() + assert_image(im, "RGB", im.size) - def _test_float_conversion(self, im): - orig = im.getpixel((5, 5)) - converted = im.convert("F").getpixel((5, 5)) - assert orig == converted - def test_8bit(self): - with Image.open("Tests/images/hopper.jpg") as im: - self._test_float_conversion(im.convert("L")) +# ref https://github.com/python-pillow/Pillow/issues/274 - def test_16bit(self): - with Image.open("Tests/images/16bit.cropped.tif") as im: - self._test_float_conversion(im) - def test_16bit_workaround(self): - with Image.open("Tests/images/16bit.cropped.tif") as im: - self._test_float_conversion(im.convert("I")) +def _test_float_conversion(im): + orig = im.getpixel((5, 5)) + converted = im.convert("F").getpixel((5, 5)) + assert orig == converted - def test_rgba_p(self): - im = hopper("RGBA") - im.putalpha(hopper("L")) - converted = im.convert("P") - comparable = converted.convert("RGBA") +def test_8bit(): + with Image.open("Tests/images/hopper.jpg") as im: + _test_float_conversion(im.convert("L")) - assert_image_similar(im, comparable, 20) - def test_trns_p(self): - im = hopper("P") - im.info["transparency"] = 0 +def test_16bit(): + with Image.open("Tests/images/16bit.cropped.tif") as im: + _test_float_conversion(im) - f = self.tempfile("temp.png") - im_l = im.convert("L") - assert im_l.info["transparency"] == 0 # undone - im_l.save(f) +def test_16bit_workaround(): + with Image.open("Tests/images/16bit.cropped.tif") as im: + _test_float_conversion(im.convert("I")) - im_rgb = im.convert("RGB") - assert im_rgb.info["transparency"] == (0, 0, 0) # undone - im_rgb.save(f) - # ref https://github.com/python-pillow/Pillow/issues/664 +def test_rgba_p(): + im = hopper("RGBA") + im.putalpha(hopper("L")) - def test_trns_p_rgba(self): - # Arrange - im = hopper("P") - im.info["transparency"] = 128 + converted = im.convert("P") + comparable = converted.convert("RGBA") - # Act - im_rgba = im.convert("RGBA") + assert_image_similar(im, comparable, 20) - # Assert - assert "transparency" not in im_rgba.info - # https://github.com/python-pillow/Pillow/issues/2702 - assert im_rgba.palette is None - def test_trns_l(self): - im = hopper("L") - im.info["transparency"] = 128 +def test_trns_p(tmp_path): + im = hopper("P") + im.info["transparency"] = 0 - f = self.tempfile("temp.png") + f = str(tmp_path / "temp.png") - im_rgb = im.convert("RGB") - assert im_rgb.info["transparency"] == (128, 128, 128) # undone - im_rgb.save(f) + im_l = im.convert("L") + assert im_l.info["transparency"] == 0 # undone + im_l.save(f) + im_rgb = im.convert("RGB") + assert im_rgb.info["transparency"] == (0, 0, 0) # undone + im_rgb.save(f) + + +# ref https://github.com/python-pillow/Pillow/issues/664 + + +def test_trns_p_rgba(): + # Arrange + im = hopper("P") + im.info["transparency"] = 128 + + # Act + im_rgba = im.convert("RGBA") + + # Assert + assert "transparency" not in im_rgba.info + # https://github.com/python-pillow/Pillow/issues/2702 + assert im_rgba.palette is None + + +def test_trns_l(tmp_path): + im = hopper("L") + im.info["transparency"] = 128 + + f = str(tmp_path / "temp.png") + + im_rgb = im.convert("RGB") + assert im_rgb.info["transparency"] == (128, 128, 128) # undone + im_rgb.save(f) + + im_p = im.convert("P") + assert "transparency" in im_p.info + im_p.save(f) + + im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE) + assert "transparency" not in im_p.info + im_p.save(f) + + +def test_trns_RGB(tmp_path): + im = hopper("RGB") + im.info["transparency"] = im.getpixel((0, 0)) + + f = str(tmp_path / "temp.png") + + im_l = im.convert("L") + assert im_l.info["transparency"] == im_l.getpixel((0, 0)) # undone + im_l.save(f) + + im_p = im.convert("P") + assert "transparency" in im_p.info + im_p.save(f) + + im_rgba = im.convert("RGBA") + assert "transparency" not in im_rgba.info + im_rgba.save(f) + + im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE) + assert "transparency" not in im_p.info + im_p.save(f) + + +def test_gif_with_rgba_palette_to_p(): + # See https://github.com/python-pillow/Pillow/issues/2433 + with Image.open("Tests/images/hopper.gif") as im: + im.info["transparency"] = 255 + im.load() + assert im.palette.mode == "RGBA" im_p = im.convert("P") - assert "transparency" in im_p.info - im_p.save(f) - im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE) - assert "transparency" not in im_p.info - im_p.save(f) + # Should not raise ValueError: unrecognized raw mode + im_p.load() - def test_trns_RGB(self): + +def test_p_la(): + im = hopper("RGBA") + alpha = hopper("L") + im.putalpha(alpha) + + comparable = im.convert("P").convert("LA").getchannel("A") + + assert_image_similar(alpha, comparable, 5) + + +def test_matrix_illegal_conversion(): + # Arrange + im = hopper("CMYK") + # fmt: off + matrix = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0) + # fmt: on + assert im.mode != "RGB" + + # Act / Assert + with pytest.raises(ValueError): + im.convert(mode="CMYK", matrix=matrix) + + +def test_matrix_wrong_mode(): + # Arrange + im = hopper("L") + # fmt: off + matrix = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0) + # fmt: on + assert im.mode == "L" + + # Act / Assert + with pytest.raises(ValueError): + im.convert(mode="L", matrix=matrix) + + +def test_matrix_xyz(): + def matrix_convert(mode): + # Arrange im = hopper("RGB") - im.info["transparency"] = im.getpixel((0, 0)) - - f = self.tempfile("temp.png") - - im_l = im.convert("L") - assert im_l.info["transparency"] == im_l.getpixel((0, 0)) # undone - im_l.save(f) - - im_p = im.convert("P") - assert "transparency" in im_p.info - im_p.save(f) - - im_rgba = im.convert("RGBA") - assert "transparency" not in im_rgba.info - im_rgba.save(f) - - im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE) - assert "transparency" not in im_p.info - im_p.save(f) - - def test_gif_with_rgba_palette_to_p(self): - # See https://github.com/python-pillow/Pillow/issues/2433 - with Image.open("Tests/images/hopper.gif") as im: - im.info["transparency"] = 255 - im.load() - assert im.palette.mode == "RGBA" - im_p = im.convert("P") - - # Should not raise ValueError: unrecognized raw mode - im_p.load() - - def test_p_la(self): - im = hopper("RGBA") - alpha = hopper("L") - im.putalpha(alpha) - - comparable = im.convert("P").convert("LA").getchannel("A") - - assert_image_similar(alpha, comparable, 5) - - def test_matrix_illegal_conversion(self): - # Arrange - im = hopper("CMYK") + im.info["transparency"] = (255, 0, 0) # fmt: off matrix = ( 0.412453, 0.357580, 0.180423, 0, 0.212671, 0.715160, 0.072169, 0, 0.019334, 0.119193, 0.950227, 0) # fmt: on - assert im.mode != "RGB" - - # Act / Assert - with pytest.raises(ValueError): - im.convert(mode="CMYK", matrix=matrix) - - def test_matrix_wrong_mode(self): - # Arrange - im = hopper("L") - # fmt: off - matrix = ( - 0.412453, 0.357580, 0.180423, 0, - 0.212671, 0.715160, 0.072169, 0, - 0.019334, 0.119193, 0.950227, 0) - # fmt: on - assert im.mode == "L" - - # Act / Assert - with pytest.raises(ValueError): - im.convert(mode="L", matrix=matrix) - - def test_matrix_xyz(self): - def matrix_convert(mode): - # Arrange - im = hopper("RGB") - im.info["transparency"] = (255, 0, 0) - # fmt: off - matrix = ( - 0.412453, 0.357580, 0.180423, 0, - 0.212671, 0.715160, 0.072169, 0, - 0.019334, 0.119193, 0.950227, 0) - # fmt: on - assert im.mode == "RGB" - - # Act - # Convert an RGB image to the CIE XYZ colour space - converted_im = im.convert(mode=mode, matrix=matrix) - - # Assert - assert converted_im.mode == mode - assert converted_im.size == im.size - with Image.open("Tests/images/hopper-XYZ.png") as target: - if converted_im.mode == "RGB": - assert_image_similar(converted_im, target, 3) - assert converted_im.info["transparency"] == (105, 54, 4) - else: - assert_image_similar(converted_im, target.getchannel(0), 1) - assert converted_im.info["transparency"] == 105 - - matrix_convert("RGB") - matrix_convert("L") - - def test_matrix_identity(self): - # Arrange - im = hopper("RGB") - # fmt: off - identity_matrix = ( - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0) - # fmt: on assert im.mode == "RGB" # Act - # Convert with an identity matrix - converted_im = im.convert(mode="RGB", matrix=identity_matrix) + # Convert an RGB image to the CIE XYZ colour space + converted_im = im.convert(mode=mode, matrix=matrix) # Assert - # No change - assert_image_equal(converted_im, im) + assert converted_im.mode == mode + assert converted_im.size == im.size + with Image.open("Tests/images/hopper-XYZ.png") as target: + if converted_im.mode == "RGB": + assert_image_similar(converted_im, target, 3) + assert converted_im.info["transparency"] == (105, 54, 4) + else: + assert_image_similar(converted_im, target.getchannel(0), 1) + assert converted_im.info["transparency"] == 105 + + matrix_convert("RGB") + matrix_convert("L") + + +def test_matrix_identity(): + # Arrange + im = hopper("RGB") + # fmt: off + identity_matrix = ( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0) + # fmt: on + assert im.mode == "RGB" + + # Act + # Convert with an identity matrix + converted_im = im.convert(mode="RGB", matrix=identity_matrix) + + # Assert + # No change + assert_image_equal(converted_im, im) diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 207d5f925..fbed276b8 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -1,62 +1,63 @@ from PIL import Image, features -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImageSplit(PillowTestCase): - def test_split(self): - def split(mode): - layers = hopper(mode).split() - return [(i.mode, i.size[0], i.size[1]) for i in layers] +def test_split(): + def split(mode): + layers = hopper(mode).split() + return [(i.mode, i.size[0], i.size[1]) for i in layers] - assert split("1") == [("1", 128, 128)] - assert split("L") == [("L", 128, 128)] - assert split("I") == [("I", 128, 128)] - assert split("F") == [("F", 128, 128)] - assert split("P") == [("P", 128, 128)] - assert split("RGB") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] - assert split("RGBA") == [ - ("L", 128, 128), - ("L", 128, 128), - ("L", 128, 128), - ("L", 128, 128), - ] - assert split("CMYK") == [ - ("L", 128, 128), - ("L", 128, 128), - ("L", 128, 128), - ("L", 128, 128), - ] - assert split("YCbCr") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] + assert split("1") == [("1", 128, 128)] + assert split("L") == [("L", 128, 128)] + assert split("I") == [("I", 128, 128)] + assert split("F") == [("F", 128, 128)] + assert split("P") == [("P", 128, 128)] + assert split("RGB") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] + assert split("RGBA") == [ + ("L", 128, 128), + ("L", 128, 128), + ("L", 128, 128), + ("L", 128, 128), + ] + assert split("CMYK") == [ + ("L", 128, 128), + ("L", 128, 128), + ("L", 128, 128), + ("L", 128, 128), + ] + assert split("YCbCr") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] - def test_split_merge(self): - def split_merge(mode): - return Image.merge(mode, hopper(mode).split()) - assert_image_equal(hopper("1"), split_merge("1")) - assert_image_equal(hopper("L"), split_merge("L")) - assert_image_equal(hopper("I"), split_merge("I")) - assert_image_equal(hopper("F"), split_merge("F")) - assert_image_equal(hopper("P"), split_merge("P")) - assert_image_equal(hopper("RGB"), split_merge("RGB")) - assert_image_equal(hopper("RGBA"), split_merge("RGBA")) - assert_image_equal(hopper("CMYK"), split_merge("CMYK")) - assert_image_equal(hopper("YCbCr"), split_merge("YCbCr")) +def test_split_merge(): + def split_merge(mode): + return Image.merge(mode, hopper(mode).split()) - def test_split_open(self): - if features.check("zlib"): - test_file = self.tempfile("temp.png") - else: - test_file = self.tempfile("temp.pcx") + assert_image_equal(hopper("1"), split_merge("1")) + assert_image_equal(hopper("L"), split_merge("L")) + assert_image_equal(hopper("I"), split_merge("I")) + assert_image_equal(hopper("F"), split_merge("F")) + assert_image_equal(hopper("P"), split_merge("P")) + assert_image_equal(hopper("RGB"), split_merge("RGB")) + assert_image_equal(hopper("RGBA"), split_merge("RGBA")) + assert_image_equal(hopper("CMYK"), split_merge("CMYK")) + assert_image_equal(hopper("YCbCr"), split_merge("YCbCr")) - def split_open(mode): - hopper(mode).save(test_file) - with Image.open(test_file) as im: - return len(im.split()) - assert split_open("1") == 1 - assert split_open("L") == 1 - assert split_open("P") == 1 - assert split_open("RGB") == 3 - if features.check("zlib"): - assert split_open("RGBA") == 4 +def test_split_open(tmp_path): + if features.check("zlib"): + test_file = str(tmp_path / "temp.png") + else: + test_file = str(tmp_path / "temp.pcx") + + def split_open(mode): + hopper(mode).save(test_file) + with Image.open(test_file) as im: + return len(im.split()) + + assert split_open("1") == 1 + assert split_open("L") == 1 + assert split_open("P") == 1 + assert split_open("RGB") == 3 + if features.check("zlib"): + assert split_open("RGBA") == 4 diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index f606dead0..29771cf03 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -1,140 +1,149 @@ import pytest from PIL import Image, ImagePalette -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal -class TestImagePalette(PillowTestCase): - def test_sanity(self): +def test_sanity(): - ImagePalette.ImagePalette("RGB", list(range(256)) * 3) - with pytest.raises(ValueError): - ImagePalette.ImagePalette("RGB", list(range(256)) * 2) + ImagePalette.ImagePalette("RGB", list(range(256)) * 3) + with pytest.raises(ValueError): + ImagePalette.ImagePalette("RGB", list(range(256)) * 2) - def test_getcolor(self): - palette = ImagePalette.ImagePalette() +def test_getcolor(): - test_map = {} - for i in range(256): - test_map[palette.getcolor((i, i, i))] = i + palette = ImagePalette.ImagePalette() - assert len(test_map) == 256 - with pytest.raises(ValueError): - palette.getcolor((1, 2, 3)) + test_map = {} + for i in range(256): + test_map[palette.getcolor((i, i, i))] = i - # Test unknown color specifier - with pytest.raises(ValueError): - palette.getcolor("unknown") + assert len(test_map) == 256 + with pytest.raises(ValueError): + palette.getcolor((1, 2, 3)) - def test_file(self): + # Test unknown color specifier + with pytest.raises(ValueError): + palette.getcolor("unknown") - palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) - f = self.tempfile("temp.lut") +def test_file(tmp_path): + palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) + + f = str(tmp_path / "temp.lut") + + palette.save(f) + + p = ImagePalette.load(f) + + # load returns raw palette information + assert len(p[0]) == 768 + assert p[1] == "RGB" + + p = ImagePalette.raw(p[1], p[0]) + assert isinstance(p, ImagePalette.ImagePalette) + assert p.palette == palette.tobytes() + + +def test_make_linear_lut(): + # Arrange + black = 0 + white = 255 + + # Act + lut = ImagePalette.make_linear_lut(black, white) + + # Assert + assert isinstance(lut, list) + assert len(lut) == 256 + # Check values + for i in range(0, len(lut)): + assert lut[i] == i + + +def test_make_linear_lut_not_yet_implemented(): + # Update after FIXME + # Arrange + black = 1 + white = 255 + + # Act + with pytest.raises(NotImplementedError): + ImagePalette.make_linear_lut(black, white) + + +def test_make_gamma_lut(): + # Arrange + exp = 5 + + # Act + lut = ImagePalette.make_gamma_lut(exp) + + # Assert + assert isinstance(lut, list) + assert len(lut) == 256 + # Check a few values + assert lut[0] == 0 + assert lut[63] == 0 + assert lut[127] == 8 + assert lut[191] == 60 + assert lut[255] == 255 + + +def test_rawmode_valueerrors(tmp_path): + # Arrange + palette = ImagePalette.raw("RGB", list(range(256)) * 3) + + # Act / Assert + with pytest.raises(ValueError): + palette.tobytes() + with pytest.raises(ValueError): + palette.getcolor((1, 2, 3)) + f = str(tmp_path / "temp.lut") + with pytest.raises(ValueError): palette.save(f) - p = ImagePalette.load(f) - # load returns raw palette information - assert len(p[0]) == 768 - assert p[1] == "RGB" +def test_getdata(): + # Arrange + data_in = list(range(256)) * 3 + palette = ImagePalette.ImagePalette("RGB", data_in) - p = ImagePalette.raw(p[1], p[0]) - assert isinstance(p, ImagePalette.ImagePalette) - assert p.palette == palette.tobytes() + # Act + mode, data_out = palette.getdata() - def test_make_linear_lut(self): - # Arrange - black = 0 - white = 255 + # Assert + assert mode == "RGB;L" - # Act - lut = ImagePalette.make_linear_lut(black, white) - # Assert - assert isinstance(lut, list) - assert len(lut) == 256 - # Check values - for i in range(0, len(lut)): - assert lut[i] == i +def test_rawmode_getdata(): + # Arrange + data_in = list(range(256)) * 3 + palette = ImagePalette.raw("RGB", data_in) - def test_make_linear_lut_not_yet_implemented(self): - # Update after FIXME - # Arrange - black = 1 - white = 255 + # Act + rawmode, data_out = palette.getdata() - # Act - with pytest.raises(NotImplementedError): - ImagePalette.make_linear_lut(black, white) + # Assert + assert rawmode == "RGB" + assert data_in == data_out - def test_make_gamma_lut(self): - # Arrange - exp = 5 - # Act - lut = ImagePalette.make_gamma_lut(exp) +def test_2bit_palette(tmp_path): + # issue #2258, 2 bit palettes are corrupted. + outfile = str(tmp_path / "temp.png") - # Assert - assert isinstance(lut, list) - assert len(lut) == 256 - # Check a few values - assert lut[0] == 0 - assert lut[63] == 0 - assert lut[127] == 8 - assert lut[191] == 60 - assert lut[255] == 255 + rgb = b"\x00" * 2 + b"\x01" * 2 + b"\x02" * 2 + img = Image.frombytes("P", (6, 1), rgb) + img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB + img.save(outfile, format="PNG") - def test_rawmode_valueerrors(self): - # Arrange - palette = ImagePalette.raw("RGB", list(range(256)) * 3) + with Image.open(outfile) as reloaded: + assert_image_equal(img, reloaded) - # Act / Assert - with pytest.raises(ValueError): - palette.tobytes() - with pytest.raises(ValueError): - palette.getcolor((1, 2, 3)) - f = self.tempfile("temp.lut") - with pytest.raises(ValueError): - palette.save(f) - def test_getdata(self): - # Arrange - data_in = list(range(256)) * 3 - palette = ImagePalette.ImagePalette("RGB", data_in) - - # Act - mode, data_out = palette.getdata() - - # Assert - assert mode == "RGB;L" - - def test_rawmode_getdata(self): - # Arrange - data_in = list(range(256)) * 3 - palette = ImagePalette.raw("RGB", data_in) - - # Act - rawmode, data_out = palette.getdata() - - # Assert - assert rawmode == "RGB" - assert data_in == data_out - - def test_2bit_palette(self): - # issue #2258, 2 bit palettes are corrupted. - outfile = self.tempfile("temp.png") - - rgb = b"\x00" * 2 + b"\x01" * 2 + b"\x02" * 2 - img = Image.frombytes("P", (6, 1), rgb) - img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB - img.save(outfile, format="PNG") - - with Image.open(outfile) as reloaded: - assert_image_equal(img, reloaded) - - def test_invalid_palette(self): - with pytest.raises(IOError): - ImagePalette.load("Tests/images/hopper.jpg") +def test_invalid_palette(): + with pytest.raises(IOError): + ImagePalette.load("Tests/images/hopper.jpg") diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 4af196ce9..b3fe9df97 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -1,98 +1,105 @@ import pytest from PIL import Image, ImageSequence, TiffImagePlugin -from .helper import PillowTestCase, assert_image_equal, hopper, skip_unless_feature +from .helper import assert_image_equal, hopper, skip_unless_feature -class TestImageSequence(PillowTestCase): - def test_sanity(self): +def test_sanity(tmp_path): - test_file = self.tempfile("temp.im") + test_file = str(tmp_path / "temp.im") - im = hopper("RGB") - im.save(test_file) + im = hopper("RGB") + im.save(test_file) - seq = ImageSequence.Iterator(im) + seq = ImageSequence.Iterator(im) - index = 0 - for frame in seq: - assert_image_equal(im, frame) - assert im.tell() == index - index += 1 + index = 0 + for frame in seq: + assert_image_equal(im, frame) + assert im.tell() == index + index += 1 - assert index == 1 + assert index == 1 - with pytest.raises(AttributeError): - ImageSequence.Iterator(0) + with pytest.raises(AttributeError): + ImageSequence.Iterator(0) - def test_iterator(self): - with Image.open("Tests/images/multipage.tiff") as im: - i = ImageSequence.Iterator(im) - for index in range(0, im.n_frames): - assert i[index] == next(i) - with pytest.raises(IndexError): - i[index + 1] - with pytest.raises(StopIteration): - next(i) - def test_iterator_min_frame(self): - with Image.open("Tests/images/hopper.psd") as im: - i = ImageSequence.Iterator(im) - for index in range(1, im.n_frames): - assert i[index] == next(i) +def test_iterator(): + with Image.open("Tests/images/multipage.tiff") as im: + i = ImageSequence.Iterator(im) + for index in range(0, im.n_frames): + assert i[index] == next(i) + with pytest.raises(IndexError): + i[index + 1] + with pytest.raises(StopIteration): + next(i) - def _test_multipage_tiff(self): - with Image.open("Tests/images/multipage.tiff") as im: - for index, frame in enumerate(ImageSequence.Iterator(im)): - frame.load() - assert index == im.tell() - frame.convert("RGB") - def test_tiff(self): - self._test_multipage_tiff() +def test_iterator_min_frame(): + with Image.open("Tests/images/hopper.psd") as im: + i = ImageSequence.Iterator(im) + for index in range(1, im.n_frames): + assert i[index] == next(i) - @skip_unless_feature("libtiff") - def test_libtiff(self): - TiffImagePlugin.READ_LIBTIFF = True - self._test_multipage_tiff() - TiffImagePlugin.READ_LIBTIFF = False - def test_consecutive(self): - with Image.open("Tests/images/multipage.tiff") as im: - firstFrame = None - for frame in ImageSequence.Iterator(im): - if firstFrame is None: - firstFrame = frame.copy() - for frame in ImageSequence.Iterator(im): - assert_image_equal(frame, firstFrame) - break +def _test_multipage_tiff(): + with Image.open("Tests/images/multipage.tiff") as im: + for index, frame in enumerate(ImageSequence.Iterator(im)): + frame.load() + assert index == im.tell() + frame.convert("RGB") - def test_palette_mmap(self): - # Using mmap in ImageFile can require to reload the palette. - with Image.open("Tests/images/multipage-mmap.tiff") as im: - color1 = im.getpalette()[0:3] - im.seek(0) - color2 = im.getpalette()[0:3] - assert color1 == color2 - def test_all_frames(self): - # Test a single image - with Image.open("Tests/images/iss634.gif") as im: - ims = ImageSequence.all_frames(im) +def test_tiff(): + _test_multipage_tiff() - assert len(ims) == 42 - for i, im_frame in enumerate(ims): - assert im_frame is not im - im.seek(i) - assert_image_equal(im, im_frame) +@skip_unless_feature("libtiff") +def test_libtiff(): + TiffImagePlugin.READ_LIBTIFF = True + _test_multipage_tiff() + TiffImagePlugin.READ_LIBTIFF = False - # Test a series of images - ims = ImageSequence.all_frames([im, hopper(), im]) - assert len(ims) == 85 - # Test an operation - ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90)) - for i, im_frame in enumerate(ims): - im.seek(i) - assert_image_equal(im.rotate(90), im_frame) +def test_consecutive(): + with Image.open("Tests/images/multipage.tiff") as im: + firstFrame = None + for frame in ImageSequence.Iterator(im): + if firstFrame is None: + firstFrame = frame.copy() + for frame in ImageSequence.Iterator(im): + assert_image_equal(frame, firstFrame) + break + + +def test_palette_mmap(): + # Using mmap in ImageFile can require to reload the palette. + with Image.open("Tests/images/multipage-mmap.tiff") as im: + color1 = im.getpalette()[0:3] + im.seek(0) + color2 = im.getpalette()[0:3] + assert color1 == color2 + + +def test_all_frames(): + # Test a single image + with Image.open("Tests/images/iss634.gif") as im: + ims = ImageSequence.all_frames(im) + + assert len(ims) == 42 + for i, im_frame in enumerate(ims): + assert im_frame is not im + + im.seek(i) + assert_image_equal(im, im_frame) + + # Test a series of images + ims = ImageSequence.all_frames([im, hopper(), im]) + assert len(ims) == 85 + + # Test an operation + ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90)) + for i, im_frame in enumerate(ims): + im.seek(i) + assert_image_equal(im.rotate(90), im_frame) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index a044d04b3..707284d7b 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -3,56 +3,58 @@ from fractions import Fraction from PIL import Image, TiffImagePlugin, features from PIL.TiffImagePlugin import IFDRational -from .helper import PillowTestCase, hopper +from .helper import hopper -class Test_IFDRational(PillowTestCase): - def _test_equal(self, num, denom, target): +def _test_equal(num, denom, target): - t = IFDRational(num, denom) + t = IFDRational(num, denom) - assert target == t - assert t == target + assert target == t + assert t == target - def test_sanity(self): - self._test_equal(1, 1, 1) - self._test_equal(1, 1, Fraction(1, 1)) +def test_sanity(): - self._test_equal(2, 2, 1) - self._test_equal(1.0, 1, Fraction(1, 1)) + _test_equal(1, 1, 1) + _test_equal(1, 1, Fraction(1, 1)) - self._test_equal(Fraction(1, 1), 1, Fraction(1, 1)) - self._test_equal(IFDRational(1, 1), 1, 1) + _test_equal(2, 2, 1) + _test_equal(1.0, 1, Fraction(1, 1)) - self._test_equal(1, 2, Fraction(1, 2)) - self._test_equal(1, 2, IFDRational(1, 2)) + _test_equal(Fraction(1, 1), 1, Fraction(1, 1)) + _test_equal(IFDRational(1, 1), 1, 1) - def test_nonetype(self): - # Fails if the _delegate function doesn't return a valid function + _test_equal(1, 2, Fraction(1, 2)) + _test_equal(1, 2, IFDRational(1, 2)) - xres = IFDRational(72) - yres = IFDRational(72) - assert xres._val is not None - assert xres.numerator is not None - assert xres.denominator is not None - assert yres._val is not None - assert xres and 1 - assert xres and yres +def test_nonetype(): + # Fails if the _delegate function doesn't return a valid function - def test_ifd_rational_save(self): - methods = (True, False) - if not features.check("libtiff"): - methods = (False,) + xres = IFDRational(72) + yres = IFDRational(72) + assert xres._val is not None + assert xres.numerator is not None + assert xres.denominator is not None + assert yres._val is not None - for libtiff in methods: - TiffImagePlugin.WRITE_LIBTIFF = libtiff + assert xres and 1 + assert xres and yres - im = hopper() - out = self.tempfile("temp.tiff") - res = IFDRational(301, 1) - im.save(out, dpi=(res, res), compression="raw") - with Image.open(out) as reloaded: - assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) +def test_ifd_rational_save(tmp_path): + methods = (True, False) + if not features.check("libtiff"): + methods = (False,) + + for libtiff in methods: + TiffImagePlugin.WRITE_LIBTIFF = libtiff + + im = hopper() + out = str(tmp_path / "temp.tiff") + res = IFDRational(301, 1) + im.save(out, dpi=(res, res), compression="raw") + + with Image.open(out) as reloaded: + assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) diff --git a/Tests/test_uploader.py b/Tests/test_uploader.py index 24e7bebc8..720926e53 100644 --- a/Tests/test_uploader.py +++ b/Tests/test_uploader.py @@ -1,13 +1,13 @@ -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper -class TestUploader(PillowTestCase): - def check_upload_equal(self): - result = hopper("P").convert("RGB") - target = hopper("RGB") - assert_image_equal(result, target) +def check_upload_equal(): + result = hopper("P").convert("RGB") + target = hopper("RGB") + assert_image_equal(result, target) - def check_upload_similar(self): - result = hopper("P").convert("RGB") - target = hopper("RGB") - assert_image_similar(result, target, 0) + +def check_upload_similar(): + result = hopper("P").convert("RGB") + target = hopper("RGB") + assert_image_similar(result, target, 0)