mirror of
https://github.com/python-pillow/Pillow.git
synced 2026-02-04 06:25:52 +03:00
Merge 3394dc8899 into 29ff5fcb55
This commit is contained in:
commit
a8fa89fbfd
|
|
@ -604,3 +604,37 @@ def test_autocontrast_preserve_one_color(color: tuple[int, int, int]) -> None:
|
|||
img, cutoff=10, preserve_tone=True
|
||||
) # single color 10 cutoff
|
||||
assert_image_equal(img, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode", ("L", "RGB"))
|
||||
def test_sepia_size_and_mode(mode: str) -> None:
|
||||
img = Image.new(mode, (10, 10))
|
||||
out = ImageOps.sepia(img)
|
||||
|
||||
assert out.mode == "RGB"
|
||||
assert out.size == img.size
|
||||
|
||||
|
||||
def test_sobel_detects_edge() -> None:
|
||||
img = Image.new("L", (5, 5))
|
||||
for x in range(3, 5):
|
||||
img.putpixel((x, 2), 255)
|
||||
|
||||
out = ImageOps.sobel(img)
|
||||
assert max(out.tobytes()) > 0
|
||||
|
||||
|
||||
def test_sobel_output_mode_and_size() -> None:
|
||||
img = Image.new("RGB", (10, 10))
|
||||
out = ImageOps.sobel(img)
|
||||
|
||||
assert out.mode == "L"
|
||||
assert out.size == img.size
|
||||
|
||||
|
||||
def test_neon_effect_mode_and_size() -> None:
|
||||
img = Image.new("RGB", (20, 20))
|
||||
out = ImageOps.neon_effect(img)
|
||||
|
||||
assert out.mode == "RGB"
|
||||
assert out.size == img.size
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ only work on L and RGB images.
|
|||
.. autofunction:: posterize
|
||||
.. autofunction:: solarize
|
||||
.. autofunction:: exif_transpose
|
||||
.. autofunction:: sepia
|
||||
.. autofunction:: sobel
|
||||
.. autofunction:: neon_effect
|
||||
|
||||
.. _relative-resize:
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import re
|
|||
from collections.abc import Sequence
|
||||
from typing import Literal, Protocol, cast, overload
|
||||
|
||||
from . import ExifTags, Image, ImagePalette
|
||||
from . import ExifTags, Image, ImageFilter, ImagePalette
|
||||
|
||||
#
|
||||
# helpers
|
||||
|
|
@ -623,6 +623,101 @@ def grayscale(image: Image.Image) -> Image.Image:
|
|||
return image.convert("L")
|
||||
|
||||
|
||||
def sepia(image: Image.Image) -> Image.Image:
|
||||
"""
|
||||
Apply a sepia tone effect to an image.
|
||||
|
||||
:param image: The image to modify.
|
||||
:return: An image.
|
||||
|
||||
"""
|
||||
if image.mode != "RGB":
|
||||
image = image.convert("RGB")
|
||||
|
||||
out = Image.new("RGB", image.size)
|
||||
|
||||
for x in range(image.width):
|
||||
for y in range(image.height):
|
||||
value = image.getpixel((x, y))
|
||||
assert isinstance(value, tuple)
|
||||
r, g, b = value
|
||||
|
||||
tr = 0.393 * r + 0.769 * g + 0.189 * b
|
||||
tg = 0.349 * r + 0.686 * g + 0.168 * b
|
||||
tb = 0.272 * r + 0.534 * g + 0.131 * b
|
||||
|
||||
out.putpixel((x, y), tuple(min(255, int(c)) for c in (tr, tg, tb)))
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def sobel(image: Image.Image) -> Image.Image:
|
||||
"""
|
||||
Applies a Sobel edge-detection filter to the given image.
|
||||
|
||||
This function computes the Sobel gradient magnitude using the
|
||||
horizontal (Gx) and vertical (Gy) Sobel kernels.
|
||||
|
||||
:param image: The image to be filtered
|
||||
:return: An image.
|
||||
"""
|
||||
if image.mode != "L":
|
||||
image = image.convert("L")
|
||||
|
||||
Kx = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]
|
||||
Ky = [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
|
||||
|
||||
out = Image.new("L", image.size)
|
||||
|
||||
for x in range(1, image.width - 1):
|
||||
for y in range(1, image.height - 1):
|
||||
|
||||
gx = gy = 0.0
|
||||
|
||||
for dy in (-1, 0, 1):
|
||||
for dx in (-1, 0, 1):
|
||||
v = image.getpixel((x + dx, y + dy))
|
||||
assert isinstance(v, (int, float))
|
||||
|
||||
gx += v * Kx[dy + 1][dx + 1]
|
||||
gy += v * Ky[dy + 1][dx + 1]
|
||||
|
||||
# Approximate gradient magnitude and clamp to [0, 255]
|
||||
mag = int(min(255, abs(gx) + abs(gy)))
|
||||
out.putpixel((x, y), mag)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def neon_effect(
|
||||
image: Image.Image, color: tuple[int, int, int] = (255, 0, 255), alpha: float = 0.2
|
||||
) -> Image.Image:
|
||||
"""
|
||||
Apply a neon glow effect to an image using edge detection,
|
||||
blur-based glow generation, colorization, and alpha blending.
|
||||
It calls all auxiliary functions required to generate
|
||||
the final result.
|
||||
|
||||
:param image: Image to create the effect
|
||||
:param color: RGB color used for neon effect
|
||||
:param alpha: Controls the intensity of the neon effect. If alpha is 0.0, a copy of
|
||||
the image is returned unaltered.
|
||||
:return: An image
|
||||
"""
|
||||
edges = sobel(image).filter(ImageFilter.GaussianBlur(2))
|
||||
|
||||
# Apply a glow-enhancing mask transformation
|
||||
glow = edges.point(lambda value: 255 - ((255 - value) ** 2 // 255))
|
||||
|
||||
# Apply a color tint to the intensity mask
|
||||
neon = Image.merge(
|
||||
"RGB",
|
||||
tuple(glow.point(lambda value: min(255, int(value * c / 255))) for c in color),
|
||||
)
|
||||
|
||||
return Image.blend(image, neon, alpha)
|
||||
|
||||
|
||||
def invert(image: Image.Image) -> Image.Image:
|
||||
"""
|
||||
Invert (negate) the image.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user