diff --git a/src/safeds/data/image/containers/_image.py b/src/safeds/data/image/containers/_image.py index a74a71b04..d37119e3e 100644 --- a/src/safeds/data/image/containers/_image.py +++ b/src/safeds/data/image/containers/_image.py @@ -6,10 +6,12 @@ from pathlib import Path from typing import Any, BinaryIO +import numpy as np import PIL from PIL import ImageEnhance, ImageFilter, ImageOps from PIL.Image import Image as PillowImage from PIL.Image import open as open_image +from skimage.util import random_noise from safeds.data.image.typing import ImageFormat @@ -305,23 +307,17 @@ def adjust_brightness(self, factor: float) -> Image: image_copy._image = ImageEnhance.Brightness(image_copy._image).enhance(factor) return image_copy - def add_gaussian_noise(self, standard_deviation: float, opacity: float = 1.0) -> Image: + def add_gaussian_noise(self, standard_deviation: float) -> Image: if standard_deviation < 0: raise ValueError("Standard deviation has to be 0 or bigger.") - if opacity < 0 or opacity > 1: - raise ValueError("Opacity has to be between 0 and 1.") - - if opacity == 0: - warnings.warn( - "Opacity is 0, this will not make changes to the image.", - UserWarning, - stacklevel=2, - ) + # noinspection PyTypeChecker + image_as_array = np.asarray(self._image) + noisy_image_as_array = random_noise(image_as_array, mode="gaussian", var=standard_deviation**2, rng=42, clip=True) + noisy_image = PIL.Image.fromarray(np.uint8(255 * noisy_image_as_array)) image_copy = copy.deepcopy(self) - img_noise = PIL.Image.effect_noise((image_copy.width, image_copy.height), standard_deviation) - image_copy._image = PIL.Image.blend(image_copy._image.convert("RGB"), img_noise.convert("RGB"), opacity) + image_copy._image = noisy_image return image_copy def adjust_contrast(self, factor: float) -> Image: diff --git a/tests/resources/image/blend/blended.png b/tests/resources/image/blend/blended.png deleted file mode 100644 index 9aa5c50cc..000000000 Binary files a/tests/resources/image/blend/blended.png and /dev/null differ diff --git a/tests/resources/image/blend/original_scaled.png b/tests/resources/image/blend/original_scaled.png deleted file mode 100644 index 7500d00ae..000000000 Binary files a/tests/resources/image/blend/original_scaled.png and /dev/null differ diff --git a/tests/resources/image/noise/noise_0.7.png b/tests/resources/image/noise/noise_0.7.png new file mode 100644 index 000000000..21c78a42c Binary files /dev/null and b/tests/resources/image/noise/noise_0.7.png differ diff --git a/tests/resources/image/noise/noise_0.7_1.0.png b/tests/resources/image/noise/noise_0.7_1.0.png deleted file mode 100644 index 1b198ef28..000000000 Binary files a/tests/resources/image/noise/noise_0.7_1.0.png and /dev/null differ diff --git a/tests/resources/image/noise/noise_2.5.png b/tests/resources/image/noise/noise_2.5.png new file mode 100644 index 000000000..0fcd5a56d Binary files /dev/null and b/tests/resources/image/noise/noise_2.5.png differ diff --git a/tests/resources/image/noise/noise_2.5_0.6.png b/tests/resources/image/noise/noise_2.5_0.6.png deleted file mode 100644 index e37086ca9..000000000 Binary files a/tests/resources/image/noise/noise_2.5_0.6.png and /dev/null differ diff --git a/tests/resources/white.jpg/white.png b/tests/resources/white.jpg/white.png deleted file mode 100644 index 4849863e8..000000000 Binary files a/tests/resources/white.jpg/white.png and /dev/null differ diff --git a/tests/safeds/data/image/containers/test_image.py b/tests/safeds/data/image/containers/test_image.py index 92bf3e8b6..8cb5837ad 100644 --- a/tests/safeds/data/image/containers/test_image.py +++ b/tests/safeds/data/image/containers/test_image.py @@ -329,49 +329,29 @@ def test_should_invert_colors(self, image: Image, expected: Image) -> None: class TestGaussianNoise: @pytest.mark.parametrize( - ("image", "standard_deviation", "opacity"), + ("image", "standard_deviation"), [ - (Image.from_png_file(resolve_resource_path("image/boy.png")), 0.7, 1.0), - (Image.from_png_file(resolve_resource_path("image/boy.png")), 2.5, 0.6), + (Image.from_png_file(resolve_resource_path("image/boy.png")), 0.7), + (Image.from_png_file(resolve_resource_path("image/boy.png")), 2.5), ], ids=["one", "two"], ) - def test_should_add_noise(self, image: Image, standard_deviation: float, opacity: float) -> None: + def test_should_add_noise(self, image: Image, standard_deviation: float) -> None: expected = Image.from_png_file( - resolve_resource_path("image/noise/noise_" + str(standard_deviation) + "_" + str(opacity) + ".png"), + resolve_resource_path("image/noise/noise_" + str(standard_deviation) + ".png"), ) - image = image.add_gaussian_noise(standard_deviation, opacity) + image = image.add_gaussian_noise(standard_deviation) + assert image == expected @pytest.mark.parametrize( - ("image", "standard_deviation", "opacity"), - [(Image.from_png_file(resolve_resource_path("image/boy.png")), -1, 1.0)], + ("image", "standard_deviation"), + [(Image.from_png_file(resolve_resource_path("image/boy.png")), -1)], ids=["sigma below zero"], ) - def test_should_raise_standard_deviation(self, image: Image, standard_deviation: float, opacity: float) -> None: + def test_should_raise_standard_deviation(self, image: Image, standard_deviation: float) -> None: with pytest.raises(ValueError, match="Standard deviation has to be 0 or bigger."): - image.add_gaussian_noise(standard_deviation, opacity) - - @pytest.mark.parametrize( - ("image", "standard_deviation", "opacity"), - [ - (Image.from_png_file(resolve_resource_path("image/boy.png")), 2, -1), - (Image.from_png_file(resolve_resource_path("image/boy.png")), 2, 2), - ], - ids=["opacity below zero", "opacity above zero"], - ) - def test_should_raise_opacity(self, image: Image, standard_deviation: float, opacity: float) -> None: - with pytest.raises(ValueError, match="Opacity has to be between 0 and 1."): - image.add_gaussian_noise(standard_deviation, opacity) - - @pytest.mark.parametrize( - ("image", "standard_deviation", "opacity"), - [(Image.from_png_file(resolve_resource_path("image/boy.png")), 1, 0)], - ids=["opacity zero"], - ) - def test_should_warn(self, image: Image, standard_deviation: float, opacity: float) -> None: - with pytest.warns(UserWarning, match="Opacity is 0, this will not make changes to the image."): - image.add_gaussian_noise(standard_deviation, opacity) + image.add_gaussian_noise(standard_deviation) class TestBlur: