Skip to content

Commit

Permalink
feat: adjust brightness and contrast of image (#368)
Browse files Browse the repository at this point in the history
Closes #289, #291.

### Summary of Changes
Added features for adjusting both brightness and contrast

Co-authored-by: daniaHu <129186516+daniaHu@users.noreply.github.com>

---------

Co-authored-by: daniaHu <s5dahuss@uni-bonn.de>
Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
Co-authored-by: Philip Gutberlet <92990487+PhilipGutberlet@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 16, 2023
1 parent 3444700 commit 1752feb
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 12 deletions.
73 changes: 67 additions & 6 deletions src/safeds/data/image/containers/_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import copy
import io
import warnings
from pathlib import Path
from typing import Any, BinaryIO

Expand Down Expand Up @@ -273,9 +274,9 @@ def flip_vertically(self) -> Image:
result : Image
The flipped image.
"""
imagecopy = copy.deepcopy(self)
imagecopy._image = self._image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
return imagecopy
image_copy = copy.deepcopy(self)
image_copy._image = self._image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
return image_copy

def flip_horizontally(self) -> Image:
"""
Expand All @@ -286,9 +287,69 @@ def flip_horizontally(self) -> Image:
result : Image
The flipped image.
"""
imagecopy = copy.deepcopy(self)
imagecopy._image = self._image.transpose(PIL.Image.FLIP_LEFT_RIGHT)
return imagecopy
image_copy = copy.deepcopy(self)
image_copy._image = self._image.transpose(PIL.Image.FLIP_LEFT_RIGHT)
return image_copy

def adjust_brightness(self, factor: float) -> Image:
"""
Adjust the brightness of an image.
Parameters
----------
factor: float
The brightness factor.
1.0 will not change the brightness.
Below 1.0 will result in a darker image.
Above 1.0 will resolut in a brighter image.
Has to be bigger than or equal to 0 (black).
Returns
-------
result: Image
The Image with adjusted brightness.
"""
if factor < 0:
raise ValueError("Brightness factor has to be 0 or bigger")
elif factor == 1:
warnings.warn(
"Brightness adjustment factor is 1.0, this will not make changes to the image.",
UserWarning,
stacklevel=2,
)

image_copy = copy.deepcopy(self)
image_copy._image = ImageEnhance.Brightness(image_copy._image).enhance(factor)
return image_copy

def adjust_contrast(self, factor: float) -> Image:
"""
Adjust Contrast of image.
Parameters
----------
factor: float
If factor > 1, increase contrast of image.
If factor = 1, no changes will be made.
If factor < 1, make image greyer.
Has to be bigger than or equal to 0 (gray).
Returns
-------
New image with adjusted contrast.
"""
if factor < 0:
raise ValueError("Contrast factor has to be 0 or bigger")
elif factor == 1:
warnings.warn(
"Contrast adjustment factor is 1.0, this will not make changes to the image.",
UserWarning,
stacklevel=2,
)

image_copy = copy.deepcopy(self)
image_copy._image = ImageEnhance.Contrast(image_copy._image).enhance(factor)
return image_copy

def blur(self, radius: int = 1) -> Image:
"""
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/resources/image/brightness/to_brighten.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 58 additions & 6 deletions tests/safeds/data/image/containers/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,10 @@ def test_should_raise(self) -> None:
class TestFlipVertically:
def test_should_flip_vertically(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/original.png"))
image = image.flip_vertically()
image2 = Image.from_png_file(resolve_resource_path("image/flip_vertically.png"))
assert image == image2
image2 = image.flip_vertically()
image3 = Image.from_png_file(resolve_resource_path("image/flip_vertically.png"))
assert image != image2
assert image2 == image3

def test_should_be_original(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/original.png"))
Expand All @@ -255,16 +256,67 @@ def test_should_be_original(self) -> None:
class TestFlipHorizontally:
def test_should_flip_horizontally(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/original.png"))
image = image.flip_horizontally()
image2 = Image.from_png_file(resolve_resource_path("image/flip_horizontally.png"))
assert image == image2
image2 = image.flip_horizontally()
image3 = Image.from_png_file(resolve_resource_path("image/flip_horizontally.png"))
assert image != image2
assert image2 == image3

def test_should_be_original(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/original.png"))
image2 = image.flip_horizontally().flip_horizontally()
assert image == image2


class TestAdjustContrast:
@pytest.mark.parametrize("factor", [0.75, 5])
def test_should_adjust_contrast(self, factor: float) -> None:
image = Image.from_png_file(resolve_resource_path("image/contrast/to_adjust_contrast.png"))
image2 = image.adjust_contrast(factor)
image3 = Image.from_png_file(
resolve_resource_path("image/contrast/contrast_adjusted_by_" + str(factor) + ".png"),
)
assert image != image2
assert image2 == image3

def test_should_not_adjust_contrast(self) -> None:
with pytest.warns(
UserWarning,
match="Contrast adjustment factor is 1.0, this will not make changes to the image.",
):
image = Image.from_png_file(resolve_resource_path("image/contrast/to_adjust_contrast.png"))
image2 = image.adjust_contrast(1)
assert image == image2

def test_should_raise(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/brightness/to_brighten.png"))
with pytest.raises(ValueError, match="Contrast factor has to be 0 or bigger"):
image.adjust_contrast(-1)


class TestBrightness:
@pytest.mark.parametrize("factor", [0.5, 10])
def test_should_adjust_brightness(self, factor: float) -> None:
image = Image.from_png_file(resolve_resource_path("image/brightness/to_brighten.png"))
image2 = image.adjust_brightness(factor)
image3 = Image.from_png_file(resolve_resource_path("image/brightness/brightened_by_" + str(factor) + ".png"))
assert image != image2
assert image2 == image3

def test_should_not_brighten(self) -> None:
with pytest.warns(
UserWarning,
match="Brightness adjustment factor is 1.0, this will not make changes to the image.",
):
image = Image.from_png_file(resolve_resource_path("image/brightness/to_brighten.png"))
image2 = image.adjust_brightness(1)
assert image == image2

def test_should_raise(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/brightness/to_brighten.png"))
with pytest.raises(ValueError, match="Brightness factor has to be 0 or bigger"):
image.adjust_brightness(-1)


class TestInvertColors:
def test_should_invert_colors_png(self) -> None:
image = Image.from_png_file(resolve_resource_path("image/original.png"))
Expand Down

0 comments on commit 1752feb

Please sign in to comment.