Merge pull request #4482 from radarhere/pytest

Removed use of PillowTestCase in various tests
This commit is contained in:
Hugo van Kemenade 2020-03-22 23:40:14 +02:00 committed by GitHub
commit 291f1eb1e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1246 additions and 1192 deletions

View File

@ -7,267 +7,280 @@ import time
import pytest
from PIL import Image, PdfParser
from .helper import PillowTestCase, hopper
from .helper import hopper
class TestFilePdf(PillowTestCase):
def helper_save_as_pdf(self, mode, **kwargs):
# Arrange
im = hopper(mode)
outfile = self.tempfile("temp_" + mode + ".pdf")
def helper_save_as_pdf(tmp_path, mode, **kwargs):
# Arrange
im = hopper(mode)
outfile = str(tmp_path / ("temp_" + mode + ".pdf"))
# Act
im.save(outfile, **kwargs)
# Act
im.save(outfile, **kwargs)
# Assert
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
with PdfParser.PdfParser(outfile) as pdf:
if kwargs.get("append_images", False) or kwargs.get("append", False):
assert len(pdf.pages) > 1
else:
assert len(pdf.pages) > 0
with open(outfile, "rb") as fp:
contents = fp.read()
size = tuple(
int(d)
for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
)
assert im.size == size
# Assert
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
with PdfParser.PdfParser(outfile) as pdf:
if kwargs.get("append_images", False) or kwargs.get("append", False):
assert len(pdf.pages) > 1
else:
assert len(pdf.pages) > 0
with open(outfile, "rb") as fp:
contents = fp.read()
size = tuple(
int(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
)
assert im.size == size
return outfile
return outfile
def test_monochrome(self):
# Arrange
mode = "1"
# Act / Assert
self.helper_save_as_pdf(mode)
def test_monochrome(tmp_path):
# Arrange
mode = "1"
def test_greyscale(self):
# Arrange
mode = "L"
# Act / Assert
helper_save_as_pdf(tmp_path, mode)
# Act / Assert
self.helper_save_as_pdf(mode)
def test_rgb(self):
# Arrange
mode = "RGB"
def test_greyscale(tmp_path):
# Arrange
mode = "L"
# Act / Assert
self.helper_save_as_pdf(mode)
# Act / Assert
helper_save_as_pdf(tmp_path, mode)
def test_p_mode(self):
# Arrange
mode = "P"
# Act / Assert
self.helper_save_as_pdf(mode)
def test_rgb(tmp_path):
# Arrange
mode = "RGB"
def test_cmyk_mode(self):
# Arrange
mode = "CMYK"
# Act / Assert
helper_save_as_pdf(tmp_path, mode)
# Act / Assert
self.helper_save_as_pdf(mode)
def test_unsupported_mode(self):
im = hopper("LA")
outfile = self.tempfile("temp_LA.pdf")
def test_p_mode(tmp_path):
# Arrange
mode = "P"
with pytest.raises(ValueError):
im.save(outfile)
# Act / Assert
helper_save_as_pdf(tmp_path, mode)
def test_save_all(self):
# Single frame image
self.helper_save_as_pdf("RGB", save_all=True)
# Multiframe image
with Image.open("Tests/images/dispose_bgnd.gif") as im:
def test_cmyk_mode(tmp_path):
# Arrange
mode = "CMYK"
outfile = self.tempfile("temp.pdf")
im.save(outfile, save_all=True)
# Act / Assert
helper_save_as_pdf(tmp_path, mode)
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
# Append images
ims = [hopper()]
im.copy().save(outfile, save_all=True, append_images=ims)
def test_unsupported_mode(tmp_path):
im = hopper("LA")
outfile = str(tmp_path / "temp_LA.pdf")
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
with pytest.raises(ValueError):
im.save(outfile)
# Test appending using a generator
def imGenerator(ims):
yield from ims
im.save(outfile, save_all=True, append_images=imGenerator(ims))
def test_save_all(tmp_path):
# Single frame image
helper_save_as_pdf(tmp_path, "RGB", save_all=True)
# Multiframe image
with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = str(tmp_path / "temp.pdf")
im.save(outfile, save_all=True)
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
# Append JPEG images
with Image.open("Tests/images/flower.jpg") as jpeg:
jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()])
# Append images
ims = [hopper()]
im.copy().save(outfile, save_all=True, append_images=ims)
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
def test_multiframe_normal_save(self):
# Test saving a multiframe image without save_all
with Image.open("Tests/images/dispose_bgnd.gif") as im:
# Test appending using a generator
def imGenerator(ims):
yield from ims
outfile = self.tempfile("temp.pdf")
im.save(outfile)
im.save(outfile, save_all=True, append_images=imGenerator(ims))
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
def test_pdf_open(self):
# fail on a buffer full of null bytes
with pytest.raises(PdfParser.PdfFormatError):
PdfParser.PdfParser(buf=bytearray(65536))
# Append JPEG images
with Image.open("Tests/images/flower.jpg") as jpeg:
jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()])
# make an empty PDF object
with PdfParser.PdfParser() as empty_pdf:
assert len(empty_pdf.pages) == 0
assert len(empty_pdf.info) == 0
assert not empty_pdf.should_close_buf
assert not empty_pdf.should_close_file
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
# make a PDF file
pdf_filename = self.helper_save_as_pdf("RGB")
# open the PDF file
with PdfParser.PdfParser(filename=pdf_filename) as hopper_pdf:
def test_multiframe_normal_save(tmp_path):
# Test saving a multiframe image without save_all
with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = str(tmp_path / "temp.pdf")
im.save(outfile)
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
def test_pdf_open(tmp_path):
# fail on a buffer full of null bytes
with pytest.raises(PdfParser.PdfFormatError):
PdfParser.PdfParser(buf=bytearray(65536))
# make an empty PDF object
with PdfParser.PdfParser() as empty_pdf:
assert len(empty_pdf.pages) == 0
assert len(empty_pdf.info) == 0
assert not empty_pdf.should_close_buf
assert not empty_pdf.should_close_file
# make a PDF file
pdf_filename = helper_save_as_pdf(tmp_path, "RGB")
# open the PDF file
with PdfParser.PdfParser(filename=pdf_filename) as hopper_pdf:
assert len(hopper_pdf.pages) == 1
assert hopper_pdf.should_close_buf
assert hopper_pdf.should_close_file
# read a PDF file from a buffer with a non-zero offset
with open(pdf_filename, "rb") as f:
content = b"xyzzy" + f.read()
with PdfParser.PdfParser(buf=content, start_offset=5) as hopper_pdf:
assert len(hopper_pdf.pages) == 1
assert not hopper_pdf.should_close_buf
assert not hopper_pdf.should_close_file
# read a PDF file from an already open file
with open(pdf_filename, "rb") as f:
with PdfParser.PdfParser(f=f) as hopper_pdf:
assert len(hopper_pdf.pages) == 1
assert hopper_pdf.should_close_buf
assert hopper_pdf.should_close_file
# read a PDF file from a buffer with a non-zero offset
with open(pdf_filename, "rb") as f:
content = b"xyzzy" + f.read()
with PdfParser.PdfParser(buf=content, start_offset=5) as hopper_pdf:
assert len(hopper_pdf.pages) == 1
assert not hopper_pdf.should_close_buf
assert not hopper_pdf.should_close_file
# read a PDF file from an already open file
with open(pdf_filename, "rb") as f:
with PdfParser.PdfParser(f=f) as hopper_pdf:
assert len(hopper_pdf.pages) == 1
assert hopper_pdf.should_close_buf
assert not hopper_pdf.should_close_file
def test_pdf_append_fails_on_nonexistent_file(self):
im = hopper("RGB")
with tempfile.TemporaryDirectory() as temp_dir:
with pytest.raises(IOError):
im.save(os.path.join(temp_dir, "nonexistent.pdf"), append=True)
def test_pdf_append_fails_on_nonexistent_file():
im = hopper("RGB")
with tempfile.TemporaryDirectory() as temp_dir:
with pytest.raises(IOError):
im.save(os.path.join(temp_dir, "nonexistent.pdf"), append=True)
def check_pdf_pages_consistency(self, pdf):
pages_info = pdf.read_indirect(pdf.pages_ref)
assert b"Parent" not in pages_info
assert b"Kids" in pages_info
kids_not_used = pages_info[b"Kids"]
for page_ref in pdf.pages:
while True:
if page_ref in kids_not_used:
kids_not_used.remove(page_ref)
page_info = pdf.read_indirect(page_ref)
assert b"Parent" in page_info
page_ref = page_info[b"Parent"]
if page_ref == pdf.pages_ref:
break
assert pdf.pages_ref == page_info[b"Parent"]
assert kids_not_used == []
def test_pdf_append(self):
# make a PDF file
pdf_filename = self.helper_save_as_pdf("RGB", producer="PdfParser")
def check_pdf_pages_consistency(pdf):
pages_info = pdf.read_indirect(pdf.pages_ref)
assert b"Parent" not in pages_info
assert b"Kids" in pages_info
kids_not_used = pages_info[b"Kids"]
for page_ref in pdf.pages:
while True:
if page_ref in kids_not_used:
kids_not_used.remove(page_ref)
page_info = pdf.read_indirect(page_ref)
assert b"Parent" in page_info
page_ref = page_info[b"Parent"]
if page_ref == pdf.pages_ref:
break
assert pdf.pages_ref == page_info[b"Parent"]
assert kids_not_used == []
# open it, check pages and info
with PdfParser.PdfParser(pdf_filename, mode="r+b") as pdf:
assert len(pdf.pages) == 1
assert len(pdf.info) == 4
assert pdf.info.Title == os.path.splitext(os.path.basename(pdf_filename))[0]
assert pdf.info.Producer == "PdfParser"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf)
# append some info
pdf.info.Title = "abc"
pdf.info.Author = "def"
pdf.info.Subject = "ghi\uABCD"
pdf.info.Keywords = "qw)e\\r(ty"
pdf.info.Creator = "hopper()"
pdf.start_writing()
pdf.write_xref_and_trailer()
def test_pdf_append(tmp_path):
# make a PDF file
pdf_filename = helper_save_as_pdf(tmp_path, "RGB", producer="PdfParser")
# open it again, check pages and info again
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.pages) == 1
assert len(pdf.info) == 8
assert pdf.info.Title == "abc"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf)
# open it, check pages and info
with PdfParser.PdfParser(pdf_filename, mode="r+b") as pdf:
assert len(pdf.pages) == 1
assert len(pdf.info) == 4
assert pdf.info.Title == os.path.splitext(os.path.basename(pdf_filename))[0]
assert pdf.info.Producer == "PdfParser"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
check_pdf_pages_consistency(pdf)
# append two images
mode_CMYK = hopper("CMYK")
mode_P = hopper("P")
mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P])
# append some info
pdf.info.Title = "abc"
pdf.info.Author = "def"
pdf.info.Subject = "ghi\uABCD"
pdf.info.Keywords = "qw)e\\r(ty"
pdf.info.Creator = "hopper()"
pdf.start_writing()
pdf.write_xref_and_trailer()
# open the PDF again, check pages and info again
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.pages) == 3
assert len(pdf.info) == 8
assert PdfParser.decode_text(pdf.info[b"Title"]) == "abc"
assert pdf.info.Title == "abc"
assert pdf.info.Producer == "PdfParser"
assert pdf.info.Keywords == "qw)e\\r(ty"
assert pdf.info.Subject == "ghi\uABCD"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf)
# open it again, check pages and info again
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.pages) == 1
assert len(pdf.info) == 8
assert pdf.info.Title == "abc"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
check_pdf_pages_consistency(pdf)
def test_pdf_info(self):
# make a PDF file
pdf_filename = self.helper_save_as_pdf(
"RGB",
title="title",
author="author",
subject="subject",
keywords="keywords",
creator="creator",
producer="producer",
creationDate=time.strptime("2000", "%Y"),
modDate=time.strptime("2001", "%Y"),
)
# append two images
mode_CMYK = hopper("CMYK")
mode_P = hopper("P")
mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P])
# open it, check pages and info
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.info) == 8
assert pdf.info.Title == "title"
assert pdf.info.Author == "author"
assert pdf.info.Subject == "subject"
assert pdf.info.Keywords == "keywords"
assert pdf.info.Creator == "creator"
assert pdf.info.Producer == "producer"
assert pdf.info.CreationDate == time.strptime("2000", "%Y")
assert pdf.info.ModDate == time.strptime("2001", "%Y")
self.check_pdf_pages_consistency(pdf)
# open the PDF again, check pages and info again
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.pages) == 3
assert len(pdf.info) == 8
assert PdfParser.decode_text(pdf.info[b"Title"]) == "abc"
assert pdf.info.Title == "abc"
assert pdf.info.Producer == "PdfParser"
assert pdf.info.Keywords == "qw)e\\r(ty"
assert pdf.info.Subject == "ghi\uABCD"
assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info
check_pdf_pages_consistency(pdf)
def test_pdf_append_to_bytesio(self):
im = hopper("RGB")
f = io.BytesIO()
im.save(f, format="PDF")
initial_size = len(f.getvalue())
assert initial_size > 0
im = hopper("P")
f = io.BytesIO(f.getvalue())
im.save(f, format="PDF", append=True)
assert len(f.getvalue()) > initial_size
def test_pdf_info(tmp_path):
# make a PDF file
pdf_filename = helper_save_as_pdf(
tmp_path,
"RGB",
title="title",
author="author",
subject="subject",
keywords="keywords",
creator="creator",
producer="producer",
creationDate=time.strptime("2000", "%Y"),
modDate=time.strptime("2001", "%Y"),
)
# open it, check pages and info
with PdfParser.PdfParser(pdf_filename) as pdf:
assert len(pdf.info) == 8
assert pdf.info.Title == "title"
assert pdf.info.Author == "author"
assert pdf.info.Subject == "subject"
assert pdf.info.Keywords == "keywords"
assert pdf.info.Creator == "creator"
assert pdf.info.Producer == "producer"
assert pdf.info.CreationDate == time.strptime("2000", "%Y")
assert pdf.info.ModDate == time.strptime("2001", "%Y")
check_pdf_pages_consistency(pdf)
def test_pdf_append_to_bytesio():
im = hopper("RGB")
f = io.BytesIO()
im.save(f, format="PDF")
initial_size = len(f.getvalue())
assert initial_size > 0
im = hopper("P")
f = io.BytesIO(f.getvalue())
im.save(f, format="PDF", append=True)
assert len(f.getvalue()) > initial_size

View File

@ -5,204 +5,202 @@ from itertools import product
import pytest
from PIL import Image
from .helper import PillowTestCase, assert_image_equal, hopper
from .helper import assert_image_equal, hopper
_TGA_DIR = os.path.join("Tests", "images", "tga")
_TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common")
class TestFileTga(PillowTestCase):
_MODES = ("L", "LA", "P", "RGB", "RGBA")
_ORIGINS = ("tl", "bl")
_MODES = ("L", "LA", "P", "RGB", "RGBA")
_ORIGINS = ("tl", "bl")
_ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1}
_ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1}
def test_sanity(self):
for mode in self._MODES:
png_paths = glob(
os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower()))
)
def test_sanity(tmp_path):
for mode in _MODES:
for png_path in png_paths:
with Image.open(png_path) as reference_im:
assert reference_im.mode == mode
def roundtrip(original_im):
out = str(tmp_path / "temp.tga")
path_no_ext = os.path.splitext(png_path)[0]
for origin, rle in product(self._ORIGINS, (True, False)):
tga_path = "{}_{}_{}.tga".format(
path_no_ext, origin, "rle" if rle else "raw"
original_im.save(out, rle=rle)
with Image.open(out) as saved_im:
if rle:
assert (
saved_im.info["compression"] == original_im.info["compression"]
)
assert saved_im.info["orientation"] == original_im.info["orientation"]
if mode == "P":
assert saved_im.getpalette() == original_im.getpalette()
assert_image_equal(saved_im, original_im)
png_paths = glob(
os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower()))
)
for png_path in png_paths:
with Image.open(png_path) as reference_im:
assert reference_im.mode == mode
path_no_ext = os.path.splitext(png_path)[0]
for origin, rle in product(_ORIGINS, (True, False)):
tga_path = "{}_{}_{}.tga".format(
path_no_ext, origin, "rle" if rle else "raw"
)
with Image.open(tga_path) as original_im:
assert original_im.format == "TGA"
assert original_im.get_format_mimetype() == "image/x-tga"
if rle:
assert original_im.info["compression"] == "tga_rle"
assert (
original_im.info["orientation"]
== _ORIGIN_TO_ORIENTATION[origin]
)
if mode == "P":
assert original_im.getpalette() == reference_im.getpalette()
with Image.open(tga_path) as original_im:
assert original_im.format == "TGA"
assert original_im.get_format_mimetype() == "image/x-tga"
if rle:
assert original_im.info["compression"] == "tga_rle"
assert (
original_im.info["orientation"]
== self._ORIGIN_TO_ORIENTATION[origin]
)
if mode == "P":
assert (
original_im.getpalette()
== reference_im.getpalette()
)
assert_image_equal(original_im, reference_im)
assert_image_equal(original_im, reference_im)
roundtrip(original_im)
# Generate a new test name every time so the
# test will not fail with permission error
# on Windows.
out = self.tempfile("temp.tga")
original_im.save(out, rle=rle)
with Image.open(out) as saved_im:
if rle:
assert (
saved_im.info["compression"]
== original_im.info["compression"]
)
assert (
saved_im.info["orientation"]
== original_im.info["orientation"]
)
if mode == "P":
assert (
saved_im.getpalette()
== original_im.getpalette()
)
def test_id_field():
# tga file with id field
test_file = "Tests/images/tga_id_field.tga"
assert_image_equal(saved_im, original_im)
# Act
with Image.open(test_file) as im:
def test_id_field(self):
# tga file with id field
test_file = "Tests/images/tga_id_field.tga"
# Assert
assert im.size == (100, 100)
# Act
with Image.open(test_file) as im:
# Assert
assert im.size == (100, 100)
def test_id_field_rle():
# tga file with id field
test_file = "Tests/images/rgb32rle.tga"
def test_id_field_rle(self):
# tga file with id field
test_file = "Tests/images/rgb32rle.tga"
# Act
with Image.open(test_file) as im:
# Act
with Image.open(test_file) as im:
# Assert
assert im.size == (199, 199)
# Assert
assert im.size == (199, 199)
def test_save(self):
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
out = self.tempfile("temp.tga")
def test_save(tmp_path):
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
out = str(tmp_path / "temp.tga")
# Save
im.save(out)
with Image.open(out) as test_im:
assert test_im.size == (100, 100)
assert test_im.info["id_section"] == im.info["id_section"]
# RGBA save
im.convert("RGBA").save(out)
# Save
im.save(out)
with Image.open(out) as test_im:
assert test_im.size == (100, 100)
def test_save_wrong_mode(self):
im = hopper("PA")
out = self.tempfile("temp.tga")
with pytest.raises(OSError):
im.save(out)
def test_save_id_section(self):
test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im:
out = self.tempfile("temp.tga")
# Check there is no id section
im.save(out)
with Image.open(out) as test_im:
assert "id_section" not in test_im.info
# Save with custom id section
im.save(out, id_section=b"Test content")
with Image.open(out) as test_im:
assert test_im.info["id_section"] == b"Test content"
# Save with custom id section greater than 255 characters
id_section = b"Test content" * 25
pytest.warns(UserWarning, lambda: im.save(out, id_section=id_section))
with Image.open(out) as test_im:
assert test_im.info["id_section"] == id_section[:255]
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
# Save with no id section
im.save(out, id_section="")
with Image.open(out) as test_im:
assert "id_section" not in test_im.info
def test_save_orientation(self):
test_file = "Tests/images/rgb32rle.tga"
out = self.tempfile("temp.tga")
with Image.open(test_file) as im:
assert im.info["orientation"] == -1
im.save(out, orientation=1)
with Image.open(out) as test_im:
assert test_im.info["orientation"] == 1
def test_save_rle(self):
test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im:
assert im.info["compression"] == "tga_rle"
out = self.tempfile("temp.tga")
# Save
im.save(out)
with Image.open(out) as test_im:
assert test_im.size == (199, 199)
assert test_im.info["compression"] == "tga_rle"
# Save without compression
im.save(out, compression=None)
with Image.open(out) as test_im:
assert "compression" not in test_im.info
assert test_im.info["id_section"] == im.info["id_section"]
# RGBA save
im.convert("RGBA").save(out)
with Image.open(out) as test_im:
assert test_im.size == (199, 199)
with Image.open(out) as test_im:
assert test_im.size == (100, 100)
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
assert "compression" not in im.info
# Save with compression
im.save(out, compression="tga_rle")
with Image.open(out) as test_im:
assert test_im.info["compression"] == "tga_rle"
def test_save_wrong_mode(tmp_path):
im = hopper("PA")
out = str(tmp_path / "temp.tga")
def test_save_l_transparency(self):
# There are 559 transparent pixels in la.tga.
num_transparent = 559
with pytest.raises(OSError):
im.save(out)
in_file = "Tests/images/la.tga"
with Image.open(in_file) as im:
assert im.mode == "LA"
assert im.getchannel("A").getcolors()[0][0] == num_transparent
out = self.tempfile("temp.tga")
im.save(out)
def test_save_id_section(tmp_path):
test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im:
out = str(tmp_path / "temp.tga")
with Image.open(out) as test_im:
assert test_im.mode == "LA"
assert test_im.getchannel("A").getcolors()[0][0] == num_transparent
# Check there is no id section
im.save(out)
with Image.open(out) as test_im:
assert "id_section" not in test_im.info
assert_image_equal(im, test_im)
# Save with custom id section
im.save(out, id_section=b"Test content")
with Image.open(out) as test_im:
assert test_im.info["id_section"] == b"Test content"
# Save with custom id section greater than 255 characters
id_section = b"Test content" * 25
pytest.warns(UserWarning, lambda: im.save(out, id_section=id_section))
with Image.open(out) as test_im:
assert test_im.info["id_section"] == id_section[:255]
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
# Save with no id section
im.save(out, id_section="")
with Image.open(out) as test_im:
assert "id_section" not in test_im.info
def test_save_orientation(tmp_path):
test_file = "Tests/images/rgb32rle.tga"
out = str(tmp_path / "temp.tga")
with Image.open(test_file) as im:
assert im.info["orientation"] == -1
im.save(out, orientation=1)
with Image.open(out) as test_im:
assert test_im.info["orientation"] == 1
def test_save_rle(tmp_path):
test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im:
assert im.info["compression"] == "tga_rle"
out = str(tmp_path / "temp.tga")
# Save
im.save(out)
with Image.open(out) as test_im:
assert test_im.size == (199, 199)
assert test_im.info["compression"] == "tga_rle"
# Save without compression
im.save(out, compression=None)
with Image.open(out) as test_im:
assert "compression" not in test_im.info
# RGBA save
im.convert("RGBA").save(out)
with Image.open(out) as test_im:
assert test_im.size == (199, 199)
test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im:
assert "compression" not in im.info
# Save with compression
im.save(out, compression="tga_rle")
with Image.open(out) as test_im:
assert test_im.info["compression"] == "tga_rle"
def test_save_l_transparency(tmp_path):
# There are 559 transparent pixels in la.tga.
num_transparent = 559
in_file = "Tests/images/la.tga"
with Image.open(in_file) as im:
assert im.mode == "LA"
assert im.getchannel("A").getcolors()[0][0] == num_transparent
out = str(tmp_path / "temp.tga")
im.save(out)
with Image.open(out) as test_im:
assert test_im.mode == "LA"
assert test_im.getchannel("A").getcolors()[0][0] == num_transparent
assert_image_equal(im, test_im)

View File

@ -2,7 +2,6 @@ import pytest
from PIL import Image, WebPImagePlugin
from .helper import (
PillowTestCase,
assert_image_similar,
assert_image_similar_tofile,
hopper,
@ -17,23 +16,21 @@ except ImportError:
HAVE_WEBP = False
class TestUnsupportedWebp(PillowTestCase):
class TestUnsupportedWebp:
def test_unsupported(self):
if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = False
file_path = "Tests/images/hopper.webp"
pytest.warns(
UserWarning, lambda: self.assertRaises(IOError, Image.open, file_path)
)
pytest.warns(UserWarning, lambda: pytest.raises(IOError, Image.open, file_path))
if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = True
@skip_unless_feature("webp")
class TestFileWebp(PillowTestCase):
def setUp(self):
class TestFileWebp:
def setup_method(self):
self.rgb_mode = "RGB"
def test_version(self):
@ -57,13 +54,13 @@ class TestFileWebp(PillowTestCase):
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
def test_write_rgb(self):
def test_write_rgb(self, tmp_path):
"""
Can we write a RGB mode file to webp without error.
Does it have the bits we expect?
"""
temp_file = self.tempfile("temp.webp")
temp_file = str(tmp_path / "temp.webp")
hopper(self.rgb_mode).save(temp_file)
with Image.open(temp_file) as image:
@ -86,13 +83,13 @@ class TestFileWebp(PillowTestCase):
target = hopper(self.rgb_mode)
assert_image_similar(image, target, 12.0)
def test_write_unsupported_mode_L(self):
def test_write_unsupported_mode_L(self, tmp_path):
"""
Saving a black-and-white file to WebP format should work, and be
similar to the original file.
"""
temp_file = self.tempfile("temp.webp")
temp_file = str(tmp_path / "temp.webp")
hopper("L").save(temp_file)
with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode
@ -105,13 +102,13 @@ class TestFileWebp(PillowTestCase):
assert_image_similar(image, target, 10.0)
def test_write_unsupported_mode_P(self):
def test_write_unsupported_mode_P(self, tmp_path):
"""
Saving a palette-based file to WebP format should work, and be
similar to the original file.
"""
temp_file = self.tempfile("temp.webp")
temp_file = str(tmp_path / "temp.webp")
hopper("P").save(temp_file)
with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode
@ -146,10 +143,10 @@ class TestFileWebp(PillowTestCase):
with pytest.raises(TypeError):
_webp.WebPDecode()
def test_no_resource_warning(self):
def test_no_resource_warning(self, tmp_path):
file_path = "Tests/images/hopper.webp"
with Image.open(file_path) as image:
temp_file = self.tempfile("temp.webp")
temp_file = str(tmp_path / "temp.webp")
pytest.warns(None, image.save, temp_file)
def test_file_pointer_could_be_reused(self):
@ -160,16 +157,16 @@ class TestFileWebp(PillowTestCase):
@skip_unless_feature("webp")
@skip_unless_feature("webp_anim")
def test_background_from_gif(self):
def test_background_from_gif(self, tmp_path):
with Image.open("Tests/images/chi.gif") as im:
original_value = im.convert("RGB").getpixel((1, 1))
# Save as WEBP
out_webp = self.tempfile("temp.webp")
out_webp = str(tmp_path / "temp.webp")
im.save(out_webp, save_all=True)
# Save as GIF
out_gif = self.tempfile("temp.gif")
out_gif = str(tmp_path / "temp.gif")
Image.open(out_webp).save(out_gif)
with Image.open(out_gif) as reread:

View File

@ -1,238 +1,249 @@
import pytest
from PIL import Image, ImageMath, ImageMode
from .helper import PillowTestCase, convert_to_comparable
from .helper import convert_to_comparable
# There are several internal implementations
remarkable_factors = [
# special implementations
1,
2,
3,
4,
5,
6,
# 1xN implementation
(1, 2),
(1, 3),
(1, 4),
(1, 7),
# Nx1 implementation
(2, 1),
(3, 1),
(4, 1),
(7, 1),
# general implementation with different paths
(4, 6),
(5, 6),
(4, 7),
(5, 7),
(19, 17),
]
gradients_image = Image.open("Tests/images/radial_gradients.png")
gradients_image.load()
class TestImageReduce(PillowTestCase):
# There are several internal implementations
remarkable_factors = [
# special implementations
1,
2,
3,
4,
5,
6,
# 1xN implementation
(1, 2),
(1, 3),
(1, 4),
(1, 7),
# Nx1 implementation
(2, 1),
(3, 1),
(4, 1),
(7, 1),
# general implementation with different paths
(4, 6),
(5, 6),
(4, 7),
(5, 7),
(19, 17),
]
def test_args_factor():
im = Image.new("L", (10, 10))
@classmethod
def setUpClass(cls):
cls.gradients_image = Image.open("Tests/images/radial_gradients.png")
cls.gradients_image.load()
assert (4, 4) == im.reduce(3).size
assert (4, 10) == im.reduce((3, 1)).size
assert (10, 4) == im.reduce((1, 3)).size
def test_args_factor(self):
im = Image.new("L", (10, 10))
with pytest.raises(ValueError):
im.reduce(0)
with pytest.raises(TypeError):
im.reduce(2.0)
with pytest.raises(ValueError):
im.reduce((0, 10))
assert (4, 4) == im.reduce(3).size
assert (4, 10) == im.reduce((3, 1)).size
assert (10, 4) == im.reduce((1, 3)).size
with pytest.raises(ValueError):
im.reduce(0)
with pytest.raises(TypeError):
im.reduce(2.0)
with pytest.raises(ValueError):
im.reduce((0, 10))
def test_args_box():
im = Image.new("L", (10, 10))
def test_args_box(self):
im = Image.new("L", (10, 10))
assert (5, 5) == im.reduce(2, (0, 0, 10, 10)).size
assert (1, 1) == im.reduce(2, (5, 5, 6, 6)).size
assert (5, 5) == im.reduce(2, (0, 0, 10, 10)).size
assert (1, 1) == im.reduce(2, (5, 5, 6, 6)).size
with pytest.raises(TypeError):
im.reduce(2, "stri")
with pytest.raises(TypeError):
im.reduce(2, 2)
with pytest.raises(ValueError):
im.reduce(2, (0, 0, 11, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, 0, 10, 11))
with pytest.raises(ValueError):
im.reduce(2, (-1, 0, 10, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, -1, 10, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, 5, 10, 5))
with pytest.raises(ValueError):
im.reduce(2, (5, 0, 5, 10))
with pytest.raises(TypeError):
im.reduce(2, "stri")
with pytest.raises(TypeError):
im.reduce(2, 2)
with pytest.raises(ValueError):
im.reduce(2, (0, 0, 11, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, 0, 10, 11))
with pytest.raises(ValueError):
im.reduce(2, (-1, 0, 10, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, -1, 10, 10))
with pytest.raises(ValueError):
im.reduce(2, (0, 5, 10, 5))
with pytest.raises(ValueError):
im.reduce(2, (5, 0, 5, 10))
def test_unsupported_modes(self):
im = Image.new("P", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
def test_unsupported_modes():
im = Image.new("P", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
im = Image.new("1", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
im = Image.new("1", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
im = Image.new("I;16", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
im = Image.new("I;16", (10, 10))
with pytest.raises(ValueError):
im.reduce(3)
def get_image(self, mode):
mode_info = ImageMode.getmode(mode)
if mode_info.basetype == "L":
bands = [self.gradients_image]
for _ in mode_info.bands[1:]:
# rotate previous image
band = bands[-1].transpose(Image.ROTATE_90)
bands.append(band)
# Correct alpha channel by transforming completely transparent pixels.
# Low alpha values also emphasize error after alpha multiplication.
if mode.endswith("A"):
bands[-1] = bands[-1].point(lambda x: int(85 + x / 1.5))
im = Image.merge(mode, bands)
else:
assert len(mode_info.bands) == 1
im = self.gradients_image.convert(mode)
# change the height to make a not-square image
return im.crop((0, 0, im.width, im.height - 5))
def compare_reduce_with_box(self, im, factor):
box = (11, 13, 146, 164)
reduced = im.reduce(factor, box=box)
reference = im.crop(box).reduce(factor)
assert reduced == reference
def get_image(mode):
mode_info = ImageMode.getmode(mode)
if mode_info.basetype == "L":
bands = [gradients_image]
for _ in mode_info.bands[1:]:
# rotate previous image
band = bands[-1].transpose(Image.ROTATE_90)
bands.append(band)
# Correct alpha channel by transforming completely transparent pixels.
# Low alpha values also emphasize error after alpha multiplication.
if mode.endswith("A"):
bands[-1] = bands[-1].point(lambda x: int(85 + x / 1.5))
im = Image.merge(mode, bands)
else:
assert len(mode_info.bands) == 1
im = gradients_image.convert(mode)
# change the height to make a not-square image
return im.crop((0, 0, im.width, im.height - 5))
def compare_reduce_with_reference(self, im, factor, average_diff=0.4, max_diff=1):
"""Image.reduce() should look very similar to Image.resize(BOX).
A reference image is compiled from a large source area
and possible last column and last row.
+-----------+
|..........c|
|..........c|
|..........c|
|rrrrrrrrrrp|
+-----------+
"""
reduced = im.reduce(factor)
def compare_reduce_with_box(im, factor):
box = (11, 13, 146, 164)
reduced = im.reduce(factor, box=box)
reference = im.crop(box).reduce(factor)
assert reduced == reference
if not isinstance(factor, (list, tuple)):
factor = (factor, factor)
reference = Image.new(im.mode, reduced.size)
area_size = (im.size[0] // factor[0], im.size[1] // factor[1])
area_box = (0, 0, area_size[0] * factor[0], area_size[1] * factor[1])
area = im.resize(area_size, Image.BOX, area_box)
reference.paste(area, (0, 0))
def compare_reduce_with_reference(im, factor, average_diff=0.4, max_diff=1):
"""Image.reduce() should look very similar to Image.resize(BOX).
if area_size[0] < reduced.size[0]:
assert reduced.size[0] - area_size[0] == 1
last_column_box = (area_box[2], 0, im.size[0], area_box[3])
last_column = im.resize((1, area_size[1]), Image.BOX, last_column_box)
reference.paste(last_column, (area_size[0], 0))
A reference image is compiled from a large source area
and possible last column and last row.
+-----------+
|..........c|
|..........c|
|..........c|
|rrrrrrrrrrp|
+-----------+
"""
reduced = im.reduce(factor)
if area_size[1] < reduced.size[1]:
assert reduced.size[1] - area_size[1] == 1
last_row_box = (0, area_box[3], area_box[2], im.size[1])
last_row = im.resize((area_size[0], 1), Image.BOX, last_row_box)
reference.paste(last_row, (0, area_size[1]))
if not isinstance(factor, (list, tuple)):
factor = (factor, factor)
if area_size[0] < reduced.size[0] and area_size[1] < reduced.size[1]:
last_pixel_box = (area_box[2], area_box[3], im.size[0], im.size[1])
last_pixel = im.resize((1, 1), Image.BOX, last_pixel_box)
reference.paste(last_pixel, area_size)
reference = Image.new(im.mode, reduced.size)
area_size = (im.size[0] // factor[0], im.size[1] // factor[1])
area_box = (0, 0, area_size[0] * factor[0], area_size[1] * factor[1])
area = im.resize(area_size, Image.BOX, area_box)
reference.paste(area, (0, 0))
self.assert_compare_images(reduced, reference, average_diff, max_diff)
if area_size[0] < reduced.size[0]:
assert reduced.size[0] - area_size[0] == 1
last_column_box = (area_box[2], 0, im.size[0], area_box[3])
last_column = im.resize((1, area_size[1]), Image.BOX, last_column_box)
reference.paste(last_column, (area_size[0], 0))
def assert_compare_images(self, a, b, max_average_diff, max_diff=255):
assert a.mode == b.mode, "got mode %r, expected %r" % (a.mode, b.mode)
assert a.size == b.size, "got size %r, expected %r" % (a.size, b.size)
if area_size[1] < reduced.size[1]:
assert reduced.size[1] - area_size[1] == 1
last_row_box = (0, area_box[3], area_box[2], im.size[1])
last_row = im.resize((area_size[0], 1), Image.BOX, last_row_box)
reference.paste(last_row, (0, area_size[1]))
a, b = convert_to_comparable(a, b)
if area_size[0] < reduced.size[0] and area_size[1] < reduced.size[1]:
last_pixel_box = (area_box[2], area_box[3], im.size[0], im.size[1])
last_pixel = im.resize((1, 1), Image.BOX, last_pixel_box)
reference.paste(last_pixel, area_size)
bands = ImageMode.getmode(a.mode).bands
for band, ach, bch in zip(bands, a.split(), b.split()):
ch_diff = ImageMath.eval("convert(abs(a - b), 'L')", a=ach, b=bch)
ch_hist = ch_diff.histogram()
assert_compare_images(reduced, reference, average_diff, max_diff)
average_diff = sum(i * num for i, num in enumerate(ch_hist)) / (
a.size[0] * a.size[1]
)
msg = "average pixel value difference {:.4f} > expected {:.4f} "
"for '{}' band".format(average_diff, max_average_diff, band)
assert max_average_diff >= average_diff, msg
last_diff = [i for i, num in enumerate(ch_hist) if num > 0][-1]
assert (
max_diff >= last_diff
), "max pixel value difference {} > expected {} for '{}' band".format(
last_diff, max_diff, band
)
def assert_compare_images(a, b, max_average_diff, max_diff=255):
assert a.mode == b.mode, "got mode %r, expected %r" % (a.mode, b.mode)
assert a.size == b.size, "got size %r, expected %r" % (a.size, b.size)
def test_mode_L(self):
im = self.get_image("L")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
a, b = convert_to_comparable(a, b)
def test_mode_LA(self):
im = self.get_image("LA")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor, 0.8, 5)
bands = ImageMode.getmode(a.mode).bands
for band, ach, bch in zip(bands, a.split(), b.split()):
ch_diff = ImageMath.eval("convert(abs(a - b), 'L')", a=ach, b=bch)
ch_hist = ch_diff.histogram()
# With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255))
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
average_diff = sum(i * num for i, num in enumerate(ch_hist)) / (
a.size[0] * a.size[1]
)
msg = "average pixel value difference {:.4f} > expected {:.4f} "
"for '{}' band".format(average_diff, max_average_diff, band)
assert max_average_diff >= average_diff, msg
def test_mode_La(self):
im = self.get_image("La")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
last_diff = [i for i, num in enumerate(ch_hist) if num > 0][-1]
assert (
max_diff >= last_diff
), "max pixel value difference {} > expected {} for '{}' band".format(
last_diff, max_diff, band
)
def test_mode_RGB(self):
im = self.get_image("RGB")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGBA(self):
im = self.get_image("RGBA")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor, 0.8, 5)
def test_mode_L():
im = get_image("L")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
# With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255))
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGBa(self):
im = self.get_image("RGBa")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_LA():
im = get_image("LA")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0.8, 5)
def test_mode_I(self):
im = self.get_image("I")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
# With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255))
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_F(self):
im = self.get_image("F")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor, 0, 0)
self.compare_reduce_with_box(im, factor)
def test_mode_La():
im = get_image("La")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_RGB():
im = get_image("RGB")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_RGBA():
im = get_image("RGBA")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0.8, 5)
# With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255))
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_RGBa():
im = get_image("RGBa")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_I():
im = get_image("I")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_F():
im = get_image("F")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0, 0)
compare_reduce_with_box(im, factor)

View File

@ -3,10 +3,10 @@ import math
import pytest
from PIL import Image, ImageTransform
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper
from .helper import assert_image_equal, assert_image_similar, hopper
class TestImageTransform(PillowTestCase):
class TestImageTransform:
def test_sanity(self):
im = Image.new("L", (100, 100))
@ -177,7 +177,7 @@ class TestImageTransform(PillowTestCase):
im.transform((100, 100), Image.EXTENT, (0, 0, w, h), resample)
class TestImageTransformAffine(PillowTestCase):
class TestImageTransformAffine:
transform = Image.AFFINE
def _test_image(self):

View File

@ -4,7 +4,6 @@ import pytest
from PIL import EpsImagePlugin, Image, ImageFile, features
from .helper import (
PillowTestCase,
assert_image,
assert_image_equal,
assert_image_similar,
@ -19,7 +18,7 @@ MAXBLOCK = ImageFile.MAXBLOCK
SAFEBLOCK = ImageFile.SAFEBLOCK
class TestImageFile(PillowTestCase):
class TestImageFile:
def test_parser(self):
def roundtrip(format):
@ -163,7 +162,7 @@ class MockImageFile(ImageFile.ImageFile):
self.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 32, None)]
class TestPyDecoder(PillowTestCase):
class TestPyDecoder:
def get_decoder(self):
decoder = MockPyDecoder(None)
@ -239,7 +238,7 @@ class TestPyDecoder(PillowTestCase):
assert im.format is None
assert im.get_format_mimetype() is None
def test_exif_jpeg(self):
def test_exif_jpeg(self, tmp_path):
with Image.open("Tests/images/exif-72dpi-int.jpg") as im: # Little endian
exif = im.getexif()
assert 258 not in exif
@ -247,7 +246,7 @@ class TestPyDecoder(PillowTestCase):
assert exif[40963] == 450
assert exif[11] == "gThumb 3.0.1"
out = self.tempfile("temp.jpg")
out = str(tmp_path / "temp.jpg")
exif[258] = 8
del exif[40960]
exif[40963] = 455
@ -267,7 +266,7 @@ class TestPyDecoder(PillowTestCase):
assert exif[40963] == 200
assert exif[305] == "Adobe Photoshop CC 2017 (Macintosh)"
out = self.tempfile("temp.jpg")
out = str(tmp_path / "temp.jpg")
exif[258] = 8
del exif[34665]
exif[40963] = 455
@ -282,12 +281,12 @@ class TestPyDecoder(PillowTestCase):
@skip_unless_feature("webp")
@skip_unless_feature("webp_anim")
def test_exif_webp(self):
def test_exif_webp(self, tmp_path):
with Image.open("Tests/images/hopper.webp") as im:
exif = im.getexif()
assert exif == {}
out = self.tempfile("temp.webp")
out = str(tmp_path / "temp.webp")
exif[258] = 8
exif[40963] = 455
exif[305] = "Pillow test"
@ -304,12 +303,12 @@ class TestPyDecoder(PillowTestCase):
im.save(out, exif=exif, save_all=True)
check_exif()
def test_exif_png(self):
def test_exif_png(self, tmp_path):
with Image.open("Tests/images/exif.png") as im:
exif = im.getexif()
assert exif == {274: 1}
out = self.tempfile("temp.png")
out = str(tmp_path / "temp.png")
exif[258] = 8
del exif[274]
exif[40963] = 455

View File

@ -3,7 +3,6 @@ import distutils.version
import os
import re
import shutil
import unittest
from io import BytesIO
from unittest import mock
@ -441,7 +440,7 @@ class TestImageFont(PillowTestCase):
with pytest.raises(UnicodeEncodeError):
font.getsize("")
@unittest.skipIf(is_pypy(), "failing on PyPy")
@pytest.mark.skipif(is_pypy(), reason="failing on PyPy")
def test_unicode_extended(self):
# issue #3777
text = "A\u278A\U0001F12B"
@ -478,7 +477,7 @@ class TestImageFont(PillowTestCase):
name = font.getname()
assert ("FreeMono", "Regular") == name
@unittest.skipIf(is_win32(), "requires Unix or macOS")
@pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
def test_find_linux_font(self):
# A lot of mocking here - this is more for hitting code and
# catching syntax like errors
@ -523,7 +522,7 @@ class TestImageFont(PillowTestCase):
font_directory + "/Duplicate.ttf", "Duplicate"
)
@unittest.skipIf(is_win32(), "requires Unix or macOS")
@pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
def test_find_macos_font(self):
# Like the linux test, more cover hitting code rather than testing
# correctness.

View File

@ -1,193 +1,207 @@
import pytest
from PIL import Image, ImageDraw, ImageFont
from .helper import PillowTestCase, assert_image_similar, skip_unless_feature
from .helper import assert_image_similar, skip_unless_feature
FONT_SIZE = 20
FONT_PATH = "Tests/fonts/DejaVuSans.ttf"
pytestmark = skip_unless_feature("raqm")
@skip_unless_feature("raqm")
class TestImagecomplextext(PillowTestCase):
def test_english(self):
# smoke test, this should not fail
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "TEST", font=ttf, fill=500, direction="ltr")
def test_complex_text(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
def test_english():
# smoke test, this should not fail
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "TEST", font=ttf, fill=500, direction="ltr")
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "اهلا عمان", font=ttf, fill=500)
target = "Tests/images/test_text.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_complex_text():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
def test_y_offset(self):
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "اهلا عمان", font=ttf, fill=500)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "العالم العربي", font=ttf, fill=500)
target = "Tests/images/test_text.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
target = "Tests/images/test_y_offset.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.7)
def test_complex_unicode_text(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
def test_y_offset():
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "السلام عليكم", font=ttf, fill=500)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "العالم العربي", font=ttf, fill=500)
target = "Tests/images/test_complex_unicode_text.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
target = "Tests/images/test_y_offset.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.7)
ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf", FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "លោកុប្បត្តិ", font=ttf, fill=500)
def test_complex_unicode_text():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_complex_unicode_text2.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 2.3)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "السلام عليكم", font=ttf, fill=500)
def test_text_direction_rtl(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_complex_unicode_text.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "English عربي", font=ttf, fill=500, direction="rtl")
ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf", FONT_SIZE)
target = "Tests/images/test_direction_rtl.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "លោកុប្បត្តិ", font=ttf, fill=500)
def test_text_direction_ltr(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_complex_unicode_text2.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 2.3)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "سلطنة عمان Oman", font=ttf, fill=500, direction="ltr")
target = "Tests/images/test_direction_ltr.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_text_direction_rtl():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
def test_text_direction_rtl2(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "English عربي", font=ttf, fill=500, direction="rtl")
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "Oman سلطنة عمان", font=ttf, fill=500, direction="rtl")
target = "Tests/images/test_direction_rtl.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
target = "Tests/images/test_direction_ltr.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_text_direction_ttb(self):
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE)
def test_text_direction_ltr():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(100, 300))
draw = ImageDraw.Draw(im)
try:
draw.text((0, 0), "English あい", font=ttf, fill=500, direction="ttb")
except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
self.skipTest("libraqm 0.7 or greater not available")
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "سلطنة عمان Oman", font=ttf, fill=500, direction="ltr")
target = "Tests/images/test_direction_ttb.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.15)
target = "Tests/images/test_direction_ltr.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_text_direction_ttb_stroke(self):
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50)
im = Image.new(mode="RGB", size=(100, 300))
draw = ImageDraw.Draw(im)
try:
draw.text(
(25, 25),
"あい",
font=ttf,
fill=500,
direction="ttb",
stroke_width=2,
stroke_fill="#0f0",
)
except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
self.skipTest("libraqm 0.7 or greater not available")
def test_text_direction_rtl2():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_direction_ttb_stroke.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 12.4)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "Oman سلطنة عمان", font=ttf, fill=500, direction="rtl")
def test_ligature_features(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_direction_ltr.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "filling", font=ttf, fill=500, features=["-liga"])
target = "Tests/images/test_ligature_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
liga_size = ttf.getsize("fi", features=["-liga"])
assert liga_size == (13, 19)
def test_text_direction_ttb():
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE)
def test_kerning_features(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(100, 300))
draw = ImageDraw.Draw(im)
try:
draw.text((0, 0), "English あい", font=ttf, fill=500, direction="ttb")
except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
pytest.skip("libraqm 0.7 or greater not available")
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "TeToAV", font=ttf, fill=500, features=["-kern"])
target = "Tests/images/test_direction_ttb.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.15)
target = "Tests/images/test_kerning_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_arabictext_features(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
def test_text_direction_ttb_stroke():
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
im = Image.new(mode="RGB", size=(100, 300))
draw = ImageDraw.Draw(im)
try:
draw.text(
(0, 0),
"اللغة العربية",
(25, 25),
"あい",
font=ttf,
fill=500,
features=["-fina", "-init", "-medi"],
direction="ttb",
stroke_width=2,
stroke_fill="#0f0",
)
except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
pytest.skip("libraqm 0.7 or greater not available")
target = "Tests/images/test_arabictext_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
target = "Tests/images/test_direction_ttb_stroke.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 12.4)
def test_x_max_and_y_offset(self):
ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40)
im = Image.new(mode="RGB", size=(50, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "لح", font=ttf, fill=500)
def test_ligature_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
target = "Tests/images/test_x_max_and_y_offset.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "filling", font=ttf, fill=500, features=["-liga"])
target = "Tests/images/test_ligature_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_language(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
liga_size = ttf.getsize("fi", features=["-liga"])
assert liga_size == (13, 19)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "абвг", font=ttf, fill=500, language="sr")
target = "Tests/images/test_language.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_kerning_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "TeToAV", font=ttf, fill=500, features=["-kern"])
target = "Tests/images/test_kerning_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_arabictext_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text(
(0, 0),
"اللغة العربية",
font=ttf,
fill=500,
features=["-fina", "-init", "-medi"],
)
target = "Tests/images/test_arabictext_features.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_x_max_and_y_offset():
ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40)
im = Image.new(mode="RGB", size=(50, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "لح", font=ttf, fill=500)
target = "Tests/images/test_x_max_and_y_offset.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)
def test_language():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), "абвг", font=ttf, fill=500, language="sr")
target = "Tests/images/test_language.png"
with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5)

View File

@ -1,12 +1,14 @@
import subprocess
import sys
from .helper import PillowTestCase, assert_image
import pytest
from .helper import assert_image
try:
from PIL import ImageGrab
class TestImageGrab(PillowTestCase):
class TestImageGrab:
def test_grab(self):
for im in [
ImageGrab.grab(),
@ -39,12 +41,13 @@ $bmp = New-Object Drawing.Bitmap 200, 200
except ImportError:
class TestImageGrab(PillowTestCase):
class TestImageGrab:
@pytest.mark.skip(reason="ImageGrab ImportError")
def test_skip(self):
self.skipTest("ImportError")
pass
class TestImageGrabImport(PillowTestCase):
class TestImageGrabImport:
def test_import(self):
# Arrange
exception = None

View File

@ -2,318 +2,340 @@
import pytest
from PIL import Image, ImageMorph, _imagingmorph
from .helper import PillowTestCase, assert_image_equal, hopper
from .helper import assert_image_equal, hopper
class MorphTests(PillowTestCase):
def setUp(self):
self.A = self.string_to_img(
"""
.......
.......
..111..
..111..
..111..
.......
.......
"""
)
def string_to_img(image_string):
"""Turn a string image representation into a binary image"""
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
height = len(rows)
width = len(rows[0])
im = Image.new("L", (width, height))
for i in range(width):
for j in range(height):
c = rows[j][i]
v = c in "X1"
im.putpixel((i, j), v)
def img_to_string(self, im):
"""Turn a (small) binary image into a string representation"""
chars = ".1"
width, height = im.size
return "\n".join(
"".join(chars[im.getpixel((c, r)) > 0] for c in range(width))
for r in range(height)
)
return im
def string_to_img(self, image_string):
"""Turn a string image representation into a binary image"""
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
height = len(rows)
width = len(rows[0])
im = Image.new("L", (width, height))
for i in range(width):
for j in range(height):
c = rows[j][i]
v = c in "X1"
im.putpixel((i, j), v)
return im
A = string_to_img(
"""
.......
.......
..111..
..111..
..111..
.......
.......
"""
)
def img_string_normalize(self, im):
return self.img_to_string(self.string_to_img(im))
def assert_img_equal(self, A, B):
assert self.img_to_string(A) == self.img_to_string(B)
def img_to_string(im):
"""Turn a (small) binary image into a string representation"""
chars = ".1"
width, height = im.size
return "\n".join(
"".join(chars[im.getpixel((c, r)) > 0] for c in range(width))
for r in range(height)
)
def assert_img_equal_img_string(self, A, Bstring):
assert self.img_to_string(A) == self.img_string_normalize(Bstring)
def test_str_to_img(self):
with Image.open("Tests/images/morph_a.png") as im:
assert_image_equal(self.A, im)
def img_string_normalize(im):
return img_to_string(string_to_img(im))
def create_lut(self):
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut()
with open("Tests/images/%s.lut" % op, "wb") as f:
f.write(lut)
# create_lut()
def test_lut(self):
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
assert lb.get_lut() is None
def assert_img_equal(A, B):
assert img_to_string(A) == img_to_string(B)
lut = lb.build_lut()
with open("Tests/images/%s.lut" % op, "rb") as f:
assert lut == bytearray(f.read())
def test_no_operator_loaded(self):
mop = ImageMorph.MorphOp()
with pytest.raises(Exception) as e:
mop.apply(None)
assert str(e.value) == "No operator loaded"
with pytest.raises(Exception) as e:
mop.match(None)
assert str(e.value) == "No operator loaded"
with pytest.raises(Exception) as e:
mop.save_lut(None)
assert str(e.value) == "No operator loaded"
def assert_img_equal_img_string(A, Bstring):
assert img_to_string(A) == img_string_normalize(Bstring)
# Test the named patterns
def test_erosion8(self):
# erosion8
mop = ImageMorph.MorphOp(op_name="erosion8")
count, Aout = mop.apply(self.A)
assert count == 8
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
.......
...1...
.......
.......
.......
""",
)
def test_dialation8(self):
# dialation8
mop = ImageMorph.MorphOp(op_name="dilation8")
count, Aout = mop.apply(self.A)
assert count == 16
self.assert_img_equal_img_string(
Aout,
"""
.......
.11111.
.11111.
.11111.
.11111.
.11111.
.......
""",
)
def test_str_to_img():
with Image.open("Tests/images/morph_a.png") as im:
assert_image_equal(A, im)
def test_erosion4(self):
# erosion4
mop = ImageMorph.MorphOp(op_name="dilation4")
count, Aout = mop.apply(self.A)
assert count == 12
self.assert_img_equal_img_string(
Aout,
"""
.......
..111..
.11111.
.11111.
.11111.
..111..
.......
""",
)
def test_edge(self):
# edge
mop = ImageMorph.MorphOp(op_name="edge")
count, Aout = mop.apply(self.A)
assert count == 1
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..111..
..1.1..
..111..
.......
.......
""",
)
def test_corner(self):
# Create a corner detector pattern
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A)
assert count == 5
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..1.1..
.......
..1.1..
.......
.......
""",
)
# Test the coordinate counting with the same operator
coords = mop.match(self.A)
assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
coords = mop.get_on_pixels(Aout)
assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
def test_mirroring(self):
# Test 'M' for mirroring
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A)
assert count == 7
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..1.1..
.......
.......
.......
.......
""",
)
def test_negate(self):
# Test 'N' for negate
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A)
assert count == 8
self.assert_img_equal_img_string(
Aout,
"""
.......
.......
..1....
.......
.......
.......
.......
""",
)
def test_non_binary_images(self):
im = hopper("RGB")
mop = ImageMorph.MorphOp(op_name="erosion8")
with pytest.raises(Exception) as e:
mop.apply(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
with pytest.raises(Exception) as e:
mop.match(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
with pytest.raises(Exception) as e:
mop.get_on_pixels(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
def test_add_patterns(self):
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
assert lb.patterns == ["1:(... ... ...)->0", "4:(00. 01. ...)->1"]
new_patterns = ["M:(00. 01. ...)->1", "N:(00. 01. ...)->1"]
# Act
lb.add_patterns(new_patterns)
# Assert
assert lb.patterns == [
"1:(... ... ...)->0",
"4:(00. 01. ...)->1",
"M:(00. 01. ...)->1",
"N:(00. 01. ...)->1",
]
def test_unknown_pattern(self):
with pytest.raises(Exception):
ImageMorph.LutBuilder(op_name="unknown")
def test_pattern_syntax_error(self):
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
new_patterns = ["a pattern with a syntax error"]
lb.add_patterns(new_patterns)
# Act / Assert
with pytest.raises(Exception) as e:
lb.build_lut()
assert str(e.value) == 'Syntax error in pattern "a pattern with a syntax error"'
def test_load_invalid_mrl(self):
# Arrange
invalid_mrl = "Tests/images/hopper.png"
mop = ImageMorph.MorphOp()
# Act / Assert
with pytest.raises(Exception) as e:
mop.load_lut(invalid_mrl)
assert str(e.value) == "Wrong size operator file!"
def test_roundtrip_mrl(self):
# Arrange
tempfile = self.tempfile("temp.mrl")
mop = ImageMorph.MorphOp(op_name="corner")
initial_lut = mop.lut
# Act
mop.save_lut(tempfile)
mop.load_lut(tempfile)
# Act / Assert
assert mop.lut == initial_lut
def test_set_lut(self):
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
def create_lut():
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut()
mop = ImageMorph.MorphOp()
with open("Tests/images/%s.lut" % op, "wb") as f:
f.write(lut)
# Act
mop.set_lut(lut)
# Assert
assert mop.lut == lut
# create_lut()
def test_lut():
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op)
assert lb.get_lut() is None
def test_wrong_mode(self):
lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
imrgb = Image.new("RGB", (10, 10))
iml = Image.new("L", (10, 10))
lut = lb.build_lut()
with open("Tests/images/%s.lut" % op, "rb") as f:
assert lut == bytearray(f.read())
with pytest.raises(RuntimeError):
_imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id)
with pytest.raises(RuntimeError):
_imagingmorph.apply(bytes(lut), iml.im.id, imrgb.im.id)
def test_no_operator_loaded():
mop = ImageMorph.MorphOp()
with pytest.raises(Exception) as e:
mop.apply(None)
assert str(e.value) == "No operator loaded"
with pytest.raises(Exception) as e:
mop.match(None)
assert str(e.value) == "No operator loaded"
with pytest.raises(Exception) as e:
mop.save_lut(None)
assert str(e.value) == "No operator loaded"
with pytest.raises(RuntimeError):
_imagingmorph.match(bytes(lut), imrgb.im.id)
# Should not raise
_imagingmorph.match(bytes(lut), iml.im.id)
# Test the named patterns
def test_erosion8():
# erosion8
mop = ImageMorph.MorphOp(op_name="erosion8")
count, Aout = mop.apply(A)
assert count == 8
assert_img_equal_img_string(
Aout,
"""
.......
.......
.......
...1...
.......
.......
.......
""",
)
def test_dialation8():
# dialation8
mop = ImageMorph.MorphOp(op_name="dilation8")
count, Aout = mop.apply(A)
assert count == 16
assert_img_equal_img_string(
Aout,
"""
.......
.11111.
.11111.
.11111.
.11111.
.11111.
.......
""",
)
def test_erosion4():
# erosion4
mop = ImageMorph.MorphOp(op_name="dilation4")
count, Aout = mop.apply(A)
assert count == 12
assert_img_equal_img_string(
Aout,
"""
.......
..111..
.11111.
.11111.
.11111.
..111..
.......
""",
)
def test_edge():
# edge
mop = ImageMorph.MorphOp(op_name="edge")
count, Aout = mop.apply(A)
assert count == 1
assert_img_equal_img_string(
Aout,
"""
.......
.......
..111..
..1.1..
..111..
.......
.......
""",
)
def test_corner():
# Create a corner detector pattern
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
count, Aout = mop.apply(A)
assert count == 5
assert_img_equal_img_string(
Aout,
"""
.......
.......
..1.1..
.......
..1.1..
.......
.......
""",
)
# Test the coordinate counting with the same operator
coords = mop.match(A)
assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
coords = mop.get_on_pixels(Aout)
assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
def test_mirroring():
# Test 'M' for mirroring
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"])
count, Aout = mop.apply(A)
assert count == 7
assert_img_equal_img_string(
Aout,
"""
.......
.......
..1.1..
.......
.......
.......
.......
""",
)
def test_negate():
# Test 'N' for negate
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"])
count, Aout = mop.apply(A)
assert count == 8
assert_img_equal_img_string(
Aout,
"""
.......
.......
..1....
.......
.......
.......
.......
""",
)
def test_non_binary_images():
im = hopper("RGB")
mop = ImageMorph.MorphOp(op_name="erosion8")
with pytest.raises(Exception) as e:
mop.apply(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
with pytest.raises(Exception) as e:
mop.match(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
with pytest.raises(Exception) as e:
mop.get_on_pixels(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L"
def test_add_patterns():
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
assert lb.patterns == ["1:(... ... ...)->0", "4:(00. 01. ...)->1"]
new_patterns = ["M:(00. 01. ...)->1", "N:(00. 01. ...)->1"]
# Act
lb.add_patterns(new_patterns)
# Assert
assert lb.patterns == [
"1:(... ... ...)->0",
"4:(00. 01. ...)->1",
"M:(00. 01. ...)->1",
"N:(00. 01. ...)->1",
]
def test_unknown_pattern():
with pytest.raises(Exception):
ImageMorph.LutBuilder(op_name="unknown")
def test_pattern_syntax_error():
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
new_patterns = ["a pattern with a syntax error"]
lb.add_patterns(new_patterns)
# Act / Assert
with pytest.raises(Exception) as e:
lb.build_lut()
assert str(e.value) == 'Syntax error in pattern "a pattern with a syntax error"'
def test_load_invalid_mrl():
# Arrange
invalid_mrl = "Tests/images/hopper.png"
mop = ImageMorph.MorphOp()
# Act / Assert
with pytest.raises(Exception) as e:
mop.load_lut(invalid_mrl)
assert str(e.value) == "Wrong size operator file!"
def test_roundtrip_mrl(tmp_path):
# Arrange
tempfile = str(tmp_path / "temp.mrl")
mop = ImageMorph.MorphOp(op_name="corner")
initial_lut = mop.lut
# Act
mop.save_lut(tempfile)
mop.load_lut(tempfile)
# Act / Assert
assert mop.lut == initial_lut
def test_set_lut():
# Arrange
lb = ImageMorph.LutBuilder(op_name="corner")
lut = lb.build_lut()
mop = ImageMorph.MorphOp()
# Act
mop.set_lut(lut)
# Assert
assert mop.lut == lut
def test_wrong_mode():
lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
imrgb = Image.new("RGB", (10, 10))
iml = Image.new("L", (10, 10))
with pytest.raises(RuntimeError):
_imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id)
with pytest.raises(RuntimeError):
_imagingmorph.apply(bytes(lut), iml.im.id, imrgb.im.id)
with pytest.raises(RuntimeError):
_imagingmorph.match(bytes(lut), imrgb.im.id)
# Should not raise
_imagingmorph.match(bytes(lut), iml.im.id)

View File

@ -4,10 +4,8 @@ import struct
import pytest
from PIL import Image, ImagePath
from .helper import PillowTestCase
class TestImagePath(PillowTestCase):
class TestImagePath:
def test_path(self):
p = ImagePath.Path(list(range(10)))

View File

@ -3,12 +3,10 @@ import sys
import pytest
from PIL import Image
from .helper import PillowTestCase
X = 255
class TestLibPack(PillowTestCase):
class TestLibPack:
def assert_pack(self, mode, rawmode, data, *pixels):
"""
data - either raw bytes with data or just number of bytes in rawmode.
@ -223,7 +221,7 @@ class TestLibPack(PillowTestCase):
)
class TestLibUnpack(PillowTestCase):
class TestLibUnpack:
def assert_unpack(self, mode, rawmode, data, *pixels):
"""
data - either raw bytes with data or just number of bytes in rawmode.

View File

@ -1,105 +1,106 @@
from PIL import Image
from .helper import PillowTestCase, hopper
from .helper import hopper
original = hopper().resize((32, 32)).convert("I")
class TestModeI16(PillowTestCase):
def verify(im1):
im2 = original.copy()
assert im1.size == im2.size
pix1 = im1.load()
pix2 = im2.load()
for y in range(im1.size[1]):
for x in range(im1.size[0]):
xy = x, y
p1 = pix1[xy]
p2 = pix2[xy]
assert p1 == p2, "got {!r} from mode {} at {}, expected {!r}".format(
p1, im1.mode, xy, p2
)
original = hopper().resize((32, 32)).convert("I")
def verify(self, im1):
im2 = self.original.copy()
assert im1.size == im2.size
pix1 = im1.load()
pix2 = im2.load()
for y in range(im1.size[1]):
for x in range(im1.size[0]):
xy = x, y
p1 = pix1[xy]
p2 = pix2[xy]
assert p1 == p2, "got {!r} from mode {} at {}, expected {!r}".format(
p1, im1.mode, xy, p2
)
def test_basic(tmp_path):
# PIL 1.1 has limited support for 16-bit image data. Check that
# create/copy/transform and save works as expected.
def test_basic(self):
# PIL 1.1 has limited support for 16-bit image data. Check that
# create/copy/transform and save works as expected.
def basic(mode):
def basic(mode):
imIn = original.convert(mode)
verify(imIn)
imIn = self.original.convert(mode)
self.verify(imIn)
w, h = imIn.size
w, h = imIn.size
imOut = imIn.copy()
verify(imOut) # copy
imOut = imIn.copy()
self.verify(imOut) # copy
imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h))
verify(imOut) # transform
imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h))
self.verify(imOut) # transform
filename = str(tmp_path / "temp.im")
imIn.save(filename)
filename = self.tempfile("temp.im")
imIn.save(filename)
with Image.open(filename) as imOut:
with Image.open(filename) as imOut:
verify(imIn)
verify(imOut)
self.verify(imIn)
self.verify(imOut)
imOut = imIn.crop((0, 0, w, h))
verify(imOut)
imOut = imIn.crop((0, 0, w, h))
self.verify(imOut)
imOut = Image.new(mode, (w, h), None)
imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0))
imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0))
imOut = Image.new(mode, (w, h), None)
imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0))
imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0))
verify(imIn)
verify(imOut)
self.verify(imIn)
self.verify(imOut)
imIn = Image.new(mode, (1, 1), 1)
assert imIn.getpixel((0, 0)) == 1
imIn = Image.new(mode, (1, 1), 1)
assert imIn.getpixel((0, 0)) == 1
imIn.putpixel((0, 0), 2)
assert imIn.getpixel((0, 0)) == 2
imIn.putpixel((0, 0), 2)
assert imIn.getpixel((0, 0)) == 2
if mode == "L":
maximum = 255
else:
maximum = 32767
if mode == "L":
maximum = 255
else:
maximum = 32767
imIn = Image.new(mode, (1, 1), 256)
assert imIn.getpixel((0, 0)) == min(256, maximum)
imIn = Image.new(mode, (1, 1), 256)
assert imIn.getpixel((0, 0)) == min(256, maximum)
imIn.putpixel((0, 0), 512)
assert imIn.getpixel((0, 0)) == min(512, maximum)
imIn.putpixel((0, 0), 512)
assert imIn.getpixel((0, 0)) == min(512, maximum)
basic("L")
basic("L")
basic("I;16")
basic("I;16B")
basic("I;16L")
basic("I;16")
basic("I;16B")
basic("I;16L")
basic("I")
basic("I")
def test_tobytes(self):
def tobytes(mode):
return Image.new(mode, (1, 1), 1).tobytes()
def test_tobytes():
def tobytes(mode):
return Image.new(mode, (1, 1), 1).tobytes()
order = 1 if Image._ENDIAN == "<" else -1
order = 1 if Image._ENDIAN == "<" else -1
assert tobytes("L") == b"\x01"
assert tobytes("I;16") == b"\x01\x00"
assert tobytes("I;16B") == b"\x00\x01"
assert tobytes("I") == b"\x01\x00\x00\x00"[::order]
assert tobytes("L") == b"\x01"
assert tobytes("I;16") == b"\x01\x00"
assert tobytes("I;16B") == b"\x00\x01"
assert tobytes("I") == b"\x01\x00\x00\x00"[::order]
def test_convert(self):
im = self.original.copy()
def test_convert():
self.verify(im.convert("I;16"))
self.verify(im.convert("I;16").convert("L"))
self.verify(im.convert("I;16").convert("I"))
im = original.copy()
self.verify(im.convert("I;16B"))
self.verify(im.convert("I;16B").convert("L"))
self.verify(im.convert("I;16B").convert("I"))
verify(im.convert("I;16"))
verify(im.convert("I;16").convert("L"))
verify(im.convert("I;16").convert("I"))
verify(im.convert("I;16B"))
verify(im.convert("I;16B").convert("L"))
verify(im.convert("I;16B").convert("I"))

View File

@ -2,88 +2,89 @@ import pickle
from PIL import Image
from .helper import PillowTestCase
def helper_pickle_file(tmp_path, pickle, protocol=0, mode=None):
# Arrange
with Image.open("Tests/images/hopper.jpg") as im:
filename = str(tmp_path / "temp.pkl")
if mode:
im = im.convert(mode)
# Act
with open(filename, "wb") as f:
pickle.dump(im, f, protocol)
with open(filename, "rb") as f:
loaded_im = pickle.load(f)
# Assert
assert im == loaded_im
class TestPickle(PillowTestCase):
def helper_pickle_file(self, pickle, protocol=0, mode=None):
# Arrange
with Image.open("Tests/images/hopper.jpg") as im:
filename = self.tempfile("temp.pkl")
if mode:
im = im.convert(mode)
def helper_pickle_string(
pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None
):
with Image.open(test_file) as im:
if mode:
im = im.convert(mode)
# Act
with open(filename, "wb") as f:
pickle.dump(im, f, protocol)
with open(filename, "rb") as f:
loaded_im = pickle.load(f)
# Act
dumped_string = pickle.dumps(im, protocol)
loaded_im = pickle.loads(dumped_string)
# Assert
assert im == loaded_im
# Assert
assert im == loaded_im
def helper_pickle_string(
self, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None
):
with Image.open(test_file) as im:
if mode:
im = im.convert(mode)
# Act
dumped_string = pickle.dumps(im, protocol)
loaded_im = pickle.loads(dumped_string)
def test_pickle_image(tmp_path):
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
helper_pickle_string(pickle, protocol)
helper_pickle_file(tmp_path, pickle, protocol)
# Assert
assert im == loaded_im
def test_pickle_image(self):
# Act / Assert
def test_pickle_p_mode():
# Act / Assert
for test_file in [
"Tests/images/test-card.png",
"Tests/images/zero_bb.png",
"Tests/images/zero_bb_scale2.png",
"Tests/images/non_zero_bb.png",
"Tests/images/non_zero_bb_scale2.png",
"Tests/images/p_trns_single.png",
"Tests/images/pil123p.png",
"Tests/images/itxt_chunks.png",
]:
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol)
self.helper_pickle_file(pickle, protocol)
helper_pickle_string(pickle, protocol=protocol, test_file=test_file)
def test_pickle_p_mode(self):
# Act / Assert
for test_file in [
"Tests/images/test-card.png",
"Tests/images/zero_bb.png",
"Tests/images/zero_bb_scale2.png",
"Tests/images/non_zero_bb.png",
"Tests/images/non_zero_bb_scale2.png",
"Tests/images/p_trns_single.png",
"Tests/images/pil123p.png",
"Tests/images/itxt_chunks.png",
]:
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(
pickle, protocol=protocol, test_file=test_file
)
def test_pickle_pa_mode(self):
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="PA")
self.helper_pickle_file(pickle, protocol, mode="PA")
def test_pickle_pa_mode(tmp_path):
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
helper_pickle_string(pickle, protocol, mode="PA")
helper_pickle_file(tmp_path, pickle, protocol, mode="PA")
def test_pickle_l_mode(self):
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="L")
self.helper_pickle_file(pickle, protocol, mode="L")
def test_pickle_la_mode_with_palette(self):
# Arrange
filename = self.tempfile("temp.pkl")
with Image.open("Tests/images/hopper.jpg") as im:
im = im.convert("PA")
def test_pickle_l_mode(tmp_path):
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
helper_pickle_string(pickle, protocol, mode="L")
helper_pickle_file(tmp_path, pickle, protocol, mode="L")
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
im.mode = "LA"
with open(filename, "wb") as f:
pickle.dump(im, f, protocol)
with open(filename, "rb") as f:
loaded_im = pickle.load(f)
im.mode = "PA"
assert im == loaded_im
def test_pickle_la_mode_with_palette(tmp_path):
# Arrange
filename = str(tmp_path / "temp.pkl")
with Image.open("Tests/images/hopper.jpg") as im:
im = im.convert("PA")
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
im.mode = "LA"
with open(filename, "wb") as f:
pickle.dump(im, f, protocol)
with open(filename, "rb") as f:
loaded_im = pickle.load(f)
im.mode = "PA"
assert im == loaded_im