mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 18:56:17 +03:00
Merge pull request #5350 from elejke/master
Add preserve_tone option to autocontrast
This commit is contained in:
commit
b0b4fee796
|
@ -29,6 +29,7 @@ def test_sanity():
|
||||||
ImageOps.autocontrast(hopper("L"), cutoff=(2, 10))
|
ImageOps.autocontrast(hopper("L"), cutoff=(2, 10))
|
||||||
ImageOps.autocontrast(hopper("L"), ignore=[0, 255])
|
ImageOps.autocontrast(hopper("L"), ignore=[0, 255])
|
||||||
ImageOps.autocontrast(hopper("L"), mask=hopper("L"))
|
ImageOps.autocontrast(hopper("L"), mask=hopper("L"))
|
||||||
|
ImageOps.autocontrast(hopper("L"), preserve_tone=True)
|
||||||
|
|
||||||
ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255))
|
ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255))
|
||||||
ImageOps.colorize(hopper("L"), "black", "white")
|
ImageOps.colorize(hopper("L"), "black", "white")
|
||||||
|
@ -336,7 +337,7 @@ def test_autocontrast_mask_toy_input():
|
||||||
assert ImageStat.Stat(result_nomask).median == [128]
|
assert ImageStat.Stat(result_nomask).median == [128]
|
||||||
|
|
||||||
|
|
||||||
def test_auto_contrast_mask_real_input():
|
def test_autocontrast_mask_real_input():
|
||||||
# Test the autocontrast with a rectangular mask
|
# Test the autocontrast with a rectangular mask
|
||||||
with Image.open("Tests/images/iptc.jpg") as img:
|
with Image.open("Tests/images/iptc.jpg") as img:
|
||||||
|
|
||||||
|
@ -362,3 +363,52 @@ def test_auto_contrast_mask_real_input():
|
||||||
threshold=2,
|
threshold=2,
|
||||||
msg="autocontrast without mask pixel incorrect",
|
msg="autocontrast without mask pixel incorrect",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_autocontrast_preserve_tone():
|
||||||
|
def autocontrast(mode, preserve_tone):
|
||||||
|
im = hopper(mode)
|
||||||
|
return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
|
||||||
|
|
||||||
|
assert autocontrast("RGB", True) != autocontrast("RGB", False)
|
||||||
|
assert autocontrast("L", True) == autocontrast("L", False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_autocontrast_preserve_gradient():
|
||||||
|
gradient = Image.linear_gradient("L")
|
||||||
|
|
||||||
|
# test with a grayscale gradient that extends to 0,255.
|
||||||
|
# Should be a noop.
|
||||||
|
out = ImageOps.autocontrast(gradient, cutoff=0, preserve_tone=True)
|
||||||
|
|
||||||
|
assert_image_equal(gradient, out)
|
||||||
|
|
||||||
|
# cutoff the top and bottom
|
||||||
|
# autocontrast should make the first and last histogram entries equal
|
||||||
|
# and, with rounding, should be 10% of the image pixels
|
||||||
|
out = ImageOps.autocontrast(gradient, cutoff=10, preserve_tone=True)
|
||||||
|
hist = out.histogram()
|
||||||
|
assert hist[0] == hist[-1]
|
||||||
|
assert hist[-1] == 256 * round(256 * 0.10)
|
||||||
|
|
||||||
|
# in rgb
|
||||||
|
img = gradient.convert("RGB")
|
||||||
|
out = ImageOps.autocontrast(img, cutoff=0, preserve_tone=True)
|
||||||
|
assert_image_equal(img, out)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"color", ((255, 255, 255), (127, 255, 0), (127, 127, 127), (0, 0, 0))
|
||||||
|
)
|
||||||
|
def test_autocontrast_preserve_one_color(color):
|
||||||
|
img = Image.new("RGB", (10, 10), color)
|
||||||
|
|
||||||
|
# single color images shouldn't change
|
||||||
|
out = ImageOps.autocontrast(img, cutoff=0, preserve_tone=True)
|
||||||
|
assert_image_equal(img, out) # single color, no cutoff
|
||||||
|
|
||||||
|
# even if there is a cutoff
|
||||||
|
out = ImageOps.autocontrast(
|
||||||
|
img, cutoff=10, preserve_tone=True
|
||||||
|
) # single color 10 cutoff
|
||||||
|
assert_image_equal(img, out)
|
||||||
|
|
|
@ -83,6 +83,15 @@ be specified through a keyword argument::
|
||||||
|
|
||||||
im.save("out.tif", icc_profile=...)
|
im.save("out.tif", icc_profile=...)
|
||||||
|
|
||||||
|
|
||||||
|
ImageOps.autocontrast: preserve_tone
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The default behaviour of :py:meth:`~PIL.ImageOps.autocontrast` is to normalize
|
||||||
|
separate histograms for each color channel, changing the tone of the image. The new
|
||||||
|
``preserve_tone`` argument keeps the tone unchanged by using one luminance histogram
|
||||||
|
for all channels.
|
||||||
|
|
||||||
Security
|
Security
|
||||||
========
|
========
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ def _lut(image, lut):
|
||||||
# actions
|
# actions
|
||||||
|
|
||||||
|
|
||||||
def autocontrast(image, cutoff=0, ignore=None, mask=None):
|
def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False):
|
||||||
"""
|
"""
|
||||||
Maximize (normalize) image contrast. This function calculates a
|
Maximize (normalize) image contrast. This function calculates a
|
||||||
histogram of the input image (or mask region), removes ``cutoff`` percent of the
|
histogram of the input image (or mask region), removes ``cutoff`` percent of the
|
||||||
|
@ -77,9 +77,17 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None):
|
||||||
:param mask: Histogram used in contrast operation is computed using pixels
|
:param mask: Histogram used in contrast operation is computed using pixels
|
||||||
within the mask. If no mask is given the entire image is used
|
within the mask. If no mask is given the entire image is used
|
||||||
for histogram computation.
|
for histogram computation.
|
||||||
|
:param preserve_tone: Preserve image tone in Photoshop-like style autocontrast.
|
||||||
|
|
||||||
|
.. versionadded:: 8.2.0
|
||||||
|
|
||||||
:return: An image.
|
:return: An image.
|
||||||
"""
|
"""
|
||||||
histogram = image.histogram(mask)
|
if preserve_tone:
|
||||||
|
histogram = image.convert("L").histogram(mask)
|
||||||
|
else:
|
||||||
|
histogram = image.histogram(mask)
|
||||||
|
|
||||||
lut = []
|
lut = []
|
||||||
for layer in range(0, len(histogram), 256):
|
for layer in range(0, len(histogram), 256):
|
||||||
h = histogram[layer : layer + 256]
|
h = histogram[layer : layer + 256]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user