Skip to content

Commit 2b946f6

Browse files
authored
Merge pull request #2 from radarhere/feat-neon-and-sepia-filters
Simplified code
2 parents 74266a5 + 20b3ccb commit 2b946f6

File tree

2 files changed

+12
-129
lines changed

2 files changed

+12
-129
lines changed

Tests/test_imageops.py

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ def test_sepia_preserves_size_and_mode() -> None:
615615

616616

617617
def test_sobel_detects_edge() -> None:
618-
img = Image.new("L", (5, 5), 0)
618+
img = Image.new("L", (5, 5))
619619
for x in range(3, 5):
620620
img.putpixel((x, 2), 255)
621621

@@ -624,61 +624,13 @@ def test_sobel_detects_edge() -> None:
624624

625625

626626
def test_sobel_output_mode_and_size() -> None:
627-
img = Image.new("RGB", (10, 10), "black")
627+
img = Image.new("RGB", (10, 10))
628628
out = ImageOps.sobel(img)
629629

630630
assert out.mode == "L"
631631
assert out.size == img.size
632632

633633

634-
def test_glow_mask_preserves_mode_and_size() -> None:
635-
img = Image.new("L", (10, 10), 128)
636-
out = ImageOps._glow_mask(img)
637-
638-
assert out.mode == "L"
639-
assert out.size == img.size
640-
641-
642-
def test_glow_mask_increases_intensity() -> None:
643-
img = Image.new("L", (1, 1), 128)
644-
out = ImageOps._glow_mask(img)
645-
646-
value = out.getpixel((0, 0))
647-
assert isinstance(value, (int, float))
648-
assert value > 128
649-
650-
651-
def test_neon_colorize_output_mode() -> None:
652-
mask = Image.new("L", (5, 5), 128)
653-
out = ImageOps._neon_colorize(mask, (255, 0, 0))
654-
655-
assert out.mode == "RGB"
656-
assert out.size == mask.size
657-
658-
659-
def test_neon_colorize_red_channel_only() -> None:
660-
mask = Image.new("L", (1, 1), 255)
661-
out = ImageOps._neon_colorize(mask, (255, 0, 0))
662-
663-
assert out.getpixel((0, 0)) == (255, 0, 0)
664-
665-
666-
def test_neon_blend_alpha_zero() -> None:
667-
base = Image.new("RGB", (1, 1), (10, 20, 30))
668-
neon = Image.new("RGB", (1, 1), (200, 200, 200))
669-
670-
out = ImageOps._neon_blend(base, neon, alpha=0)
671-
assert out.getpixel((0, 0)) == (10, 20, 30)
672-
673-
674-
def test_neon_blend_alpha_one() -> None:
675-
base = Image.new("RGB", (1, 1), (10, 20, 30))
676-
neon = Image.new("RGB", (1, 1), (200, 200, 200))
677-
678-
out = ImageOps._neon_blend(base, neon, alpha=1)
679-
assert out.getpixel((0, 0)) == (200, 200, 200)
680-
681-
682634
def test_neon_effect_mode_and_size() -> None:
683635
img = Image.new("RGB", (20, 20))
684636
out = ImageOps.neon_effect(img)

src/PIL/ImageOps.py

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -689,79 +689,6 @@ def sobel(image: Image.Image) -> Image.Image:
689689
return out
690690

691691

692-
def _glow_mask(edge_img: Image.Image) -> Image.Image:
693-
"""
694-
Apply a glow-enhancing mask transformation to an edge image.
695-
696-
:param edge_img: A grayscale image containing edge intensities.
697-
:return: An image.
698-
"""
699-
700-
def screen_point(value: int) -> int:
701-
return 255 - ((255 - value) * (255 - value) // 255)
702-
703-
return edge_img.point(screen_point)
704-
705-
706-
def _neon_colorize(mask: Image.Image, color: tuple[int, int, int]) -> Image.Image:
707-
"""
708-
Apply a color tint to an intensity mask for neon/glow effects.
709-
:param mask: single-channel mask.
710-
:param color: color to be applied
711-
:return: An image
712-
"""
713-
r, g, b = color
714-
out = Image.new("RGB", mask.size)
715-
716-
for y in range(mask.height):
717-
for x in range(mask.width):
718-
v = mask.getpixel((x, y))
719-
assert isinstance(v, (int, float))
720-
721-
out.putpixel((x, y), tuple(min(255, int(v * c / 255)) for c in (r, g, b)))
722-
723-
return out
724-
725-
726-
def _neon_blend(
727-
original: Image.Image, neon: Image.Image, alpha: float = 0.55
728-
) -> Image.Image:
729-
"""
730-
Blend the original image with its neon/glow layer
731-
732-
:param original: Image to blend whith neon layer
733-
:param neon: neon Layer
734-
:param alpha: controls intensity of neon effect
735-
:return: An image
736-
"""
737-
if alpha < 0:
738-
alpha = 0
739-
if alpha > 1:
740-
alpha = 1
741-
742-
out = Image.new("RGB", original.size)
743-
744-
for y in range(original.height):
745-
for x in range(original.width):
746-
value1 = original.getpixel((x, y))
747-
value2 = neon.getpixel((x, y))
748-
assert isinstance(value1, tuple)
749-
assert isinstance(value2, tuple)
750-
r1, g1, b1 = value1
751-
r2, g2, b2 = value2
752-
753-
out.putpixel(
754-
(x, y),
755-
(
756-
int((1 - alpha) * r1 + alpha * r2),
757-
int((1 - alpha) * g1 + alpha * g2),
758-
int((1 - alpha) * b1 + alpha * b2),
759-
),
760-
)
761-
762-
return out
763-
764-
765692
def neon_effect(
766693
image: Image.Image, color: tuple[int, int, int] = (255, 0, 255), alpha: float = 0.2
767694
) -> Image.Image:
@@ -775,15 +702,19 @@ def neon_effect(
775702
:param color: RGB color used for neon effect
776703
:alpha: controls the intensity of the neon effect
777704
:return: An image
778-
779705
"""
780-
edges = sobel(image)
781-
edges = edges.filter(ImageFilter.GaussianBlur(2))
706+
edges = sobel(image).filter(ImageFilter.GaussianBlur(2))
707+
708+
# Apply a glow-enhancing mask transformation
709+
glow = edges.point(lambda value: 255 - ((255 - value) ** 2 // 255))
782710

783-
glow = _glow_mask(edges)
784-
neon = _neon_colorize(glow, color)
711+
# Apply a color tint to the intensity mask
712+
neon = Image.merge(
713+
"RGB",
714+
tuple(glow.point(lambda value: min(255, int(value * c / 255))) for c in color),
715+
)
785716

786-
return _neon_blend(image, neon, alpha)
717+
return Image.blend(image, neon, alpha)
787718

788719

789720
def invert(image: Image.Image) -> Image.Image:

0 commit comments

Comments
 (0)