diff --git a/src/safeds/data/image/_utils/_image_transformation_error_and_warning_checks.py b/src/safeds/data/image/_utils/_image_transformation_error_and_warning_checks.py index a800d54be..807a72f9c 100644 --- a/src/safeds/data/image/_utils/_image_transformation_error_and_warning_checks.py +++ b/src/safeds/data/image/_utils/_image_transformation_error_and_warning_checks.py @@ -13,20 +13,13 @@ def _check_resize_errors(new_width: int, new_height: int) -> None: _check_bounds("new_height", new_height, lower_bound=_ClosedBound(1)) -def _check_crop_errors_and_warnings( +def _check_crop_warnings( x: int, y: int, - width: int, - height: int, min_width: int, min_height: int, plural: bool, ) -> None: - _check_bounds("x", x, lower_bound=_ClosedBound(0)) - _check_bounds("y", y, lower_bound=_ClosedBound(0)) - _check_bounds("width", width, lower_bound=_ClosedBound(1)) - _check_bounds("height", height, lower_bound=_ClosedBound(1)) - if x >= min_width or y >= min_height: warnings.warn( f"The specified bounding rectangle does not contain any content of {'at least one' if plural else 'the'} image. Therefore {'these images' if plural else 'the image'} will be blank.", @@ -35,6 +28,18 @@ def _check_crop_errors_and_warnings( ) +def _check_crop_errors( + x: int, + y: int, + width: int, + height: int, +) -> None: + _check_bounds("x", x, lower_bound=_ClosedBound(0)) + _check_bounds("y", y, lower_bound=_ClosedBound(0)) + _check_bounds("width", width, lower_bound=_ClosedBound(1)) + _check_bounds("height", height, lower_bound=_ClosedBound(1)) + + def _check_adjust_brightness_errors_and_warnings(factor: float, plural: bool) -> None: _check_bounds("factor", factor, lower_bound=_ClosedBound(0)) if factor == 1: diff --git a/src/safeds/data/image/containers/_empty_image_list.py b/src/safeds/data/image/containers/_empty_image_list.py index 025a56716..48531c965 100644 --- a/src/safeds/data/image/containers/_empty_image_list.py +++ b/src/safeds/data/image/containers/_empty_image_list.py @@ -10,7 +10,7 @@ _check_adjust_color_balance_errors_and_warnings, _check_adjust_contrast_errors_and_warnings, _check_blur_errors_and_warnings, - _check_crop_errors_and_warnings, + _check_crop_errors, _check_remove_images_with_size_errors, _check_resize_errors, _check_sharpen_errors_and_warnings, @@ -161,15 +161,7 @@ def convert_to_grayscale(self) -> ImageList: def crop(self, x: int, y: int, width: int, height: int) -> ImageList: _EmptyImageList._warn_empty_image_list() - _check_crop_errors_and_warnings( - x, - y, - width, - height, - x + 1, - y + 1, - plural=True, - ) # Disable x|y >= min_width|min_height check with min_width|min_height=x|y+1 + _check_crop_errors(x, y, width, height) return _EmptyImageList() def flip_vertically(self) -> ImageList: diff --git a/src/safeds/data/image/containers/_image.py b/src/safeds/data/image/containers/_image.py index b1ce3abd8..1aecf60b0 100644 --- a/src/safeds/data/image/containers/_image.py +++ b/src/safeds/data/image/containers/_image.py @@ -14,7 +14,8 @@ _check_adjust_color_balance_errors_and_warnings, _check_adjust_contrast_errors_and_warnings, _check_blur_errors_and_warnings, - _check_crop_errors_and_warnings, + _check_crop_errors, + _check_crop_warnings, _check_resize_errors, _check_sharpen_errors_and_warnings, ) @@ -46,7 +47,7 @@ def _filter_edges_kernel() -> Tensor: if Image._filter_edges_kernel_cache is None: Image._filter_edges_kernel_cache = ( - torch.tensor([[-1.0, -1.0, -1.0], [-1.0, 8.0, -1.0], [-1.0, -1.0, -1.0]]) + torch.tensor([[-1.0, -1.0, -1.0], [-1.0, 8.0, -1.0], [-1.0, -1.0, -1.0]], dtype=torch.float16) .unsqueeze(dim=0) .unsqueeze(dim=0) .to(_get_device()) @@ -118,7 +119,13 @@ def from_bytes(data: bytes) -> Image: return Image(image_tensor=torchvision.io.decode_image(input_tensor).to(_get_device())) def __init__(self, image_tensor: Tensor) -> None: - self._image_tensor: Tensor = image_tensor + import torch + + self._image_tensor: Tensor + if image_tensor.dtype != torch.uint8: + self._image_tensor = torch.clamp(image_tensor, 0, 255).to(torch.uint8) + else: + self._image_tensor = image_tensor def __eq__(self, other: object) -> bool: """ @@ -444,18 +451,20 @@ def convert_to_grayscale(self) -> Image: _init_default_device() - if self.channel == 4: + if self.channel == 1: + return self + elif self.channel == 4: return Image( torch.cat( [ func2.rgb_to_grayscale(self._image_tensor[0:3], num_output_channels=3), - self._image_tensor[3].unsqueeze(dim=0), + self._image_tensor[3:4], ], ), ) - else: + else: # channel == 3 return Image( - func2.rgb_to_grayscale(self._image_tensor[0:3], num_output_channels=self.channel), + func2.rgb_to_grayscale(self._image_tensor[0:3], num_output_channels=3), ) def crop(self, x: int, y: int, width: int, height: int) -> Image: @@ -489,7 +498,8 @@ def crop(self, x: int, y: int, width: int, height: int) -> Image: _init_default_device() - _check_crop_errors_and_warnings(x, y, width, height, self.width, self.height, plural=False) + _check_crop_errors(x, y, width, height) + _check_crop_warnings(x, y, self.width, self.height, plural=False) return Image(func2.crop(self._image_tensor, y, x, height, width)) def flip_vertically(self) -> Image: @@ -552,22 +562,31 @@ def adjust_brightness(self, factor: float) -> Image: If factor is smaller than 0. """ import torch - from torchvision.transforms.v2 import functional as func2 _init_default_device() _check_adjust_brightness_errors_and_warnings(factor, plural=False) - if self.channel == 4: - return Image( - torch.cat( - [ - func2.adjust_brightness(self._image_tensor[0:3], factor * 1.0), - self._image_tensor[3].unsqueeze(dim=0), - ], - ), - ) + if self._image_tensor.size(dim=-3) != 4: + if factor == 0: + return Image(torch.zeros(self._image_tensor.size(), dtype=torch.uint8)) + else: + temp_tensor = self._image_tensor * torch.tensor([factor * 1.0], dtype=torch.float16) + torch.clamp(temp_tensor, 0, 255, out=temp_tensor) + return Image(temp_tensor.to(torch.uint8)) else: - return Image(func2.adjust_brightness(self._image_tensor, factor * 1.0)) + img_tensor = torch.empty(self._image_tensor.size(), dtype=torch.uint8) + img_tensor[3] = self._image_tensor[3] + if factor == 0: + torch.zeros( + (3, self._image_tensor.size(dim=-2), self._image_tensor.size(dim=-1)), + dtype=torch.uint8, + out=img_tensor[:, 0:3], + ) + else: + temp_tensor = self._image_tensor[0:3] * torch.tensor([factor * 1.0], dtype=torch.float16) + torch.clamp(temp_tensor, 0, 255, out=temp_tensor) + img_tensor[0:3] = temp_tensor[:] + return Image(img_tensor) def add_noise(self, standard_deviation: float) -> Image: """ @@ -595,9 +614,12 @@ def add_noise(self, standard_deviation: float) -> Image: _init_default_device() _check_add_noise_errors(standard_deviation) - return Image( - self._image_tensor + torch.normal(0, standard_deviation, self._image_tensor.size()).to(_get_device()) * 255, - ) + float_tensor = torch.empty(self._image_tensor.size(), dtype=torch.float16) + torch.normal(0, standard_deviation, self._image_tensor.size(), out=float_tensor) + float_tensor *= 255 + float_tensor += self._image_tensor + torch.clamp(float_tensor, 0, 255, out=float_tensor) + return Image(float_tensor.to(torch.uint8)) def adjust_contrast(self, factor: float) -> Image: """ @@ -624,22 +646,26 @@ def adjust_contrast(self, factor: float) -> Image: If factor is smaller than 0. """ import torch - from torchvision.transforms.v2 import functional as func2 _init_default_device() _check_adjust_contrast_errors_and_warnings(factor, plural=False) + + factor *= 1.0 + adjusted_factor = (1 - factor) / factor + gray_tensor = self.convert_to_grayscale()._image_tensor[0] + mean = torch.mean(gray_tensor, dim=(-2, -1), dtype=torch.float16) + del gray_tensor + mean *= torch.tensor(adjusted_factor, dtype=torch.float16) + tensor = mean.repeat(min(self.channel, 3), self._image_tensor.size(dim=-2), self._image_tensor.size(dim=-1)) + tensor += self._image_tensor[0 : min(self.channel, 3)] + tensor *= factor + torch.clamp(tensor, 0, 255, out=tensor) + if self.channel == 4: - return Image( - torch.cat( - [ - func2.adjust_contrast(self._image_tensor[0:3], factor * 1.0), - self._image_tensor[3].unsqueeze(dim=0), - ], - ), - ) + return Image(torch.cat([tensor.to(torch.uint8), self._image_tensor[3:4]], dim=0)) else: - return Image(func2.adjust_contrast(self._image_tensor, factor * 1.0)) + return Image(tensor.to(torch.uint8)) def adjust_color_balance(self, factor: float) -> Image: """ @@ -665,10 +691,20 @@ def adjust_color_balance(self, factor: float) -> Image: OutOfBoundsError If factor is smaller than 0. """ + import torch + _check_adjust_color_balance_errors_and_warnings(factor, self.channel, plural=False) - return Image( - self.convert_to_grayscale()._image_tensor * (1.0 - factor * 1.0) + self._image_tensor * (factor * 1.0), - ) + + factor *= 1.0 + if factor == 0: + return self.convert_to_grayscale() + else: + adjusted_factor = (1 - factor) / factor + tensor = self.convert_to_grayscale()._image_tensor * torch.tensor(adjusted_factor, dtype=torch.float16) + tensor += self._image_tensor + tensor *= factor + torch.clamp(tensor, 0, 255, out=tensor) + return Image(tensor.to(torch.uint8)) def blur(self, radius: int) -> Image: """ @@ -692,12 +728,30 @@ def blur(self, radius: int) -> Image: OutOfBoundsError If radius is smaller than 0 or equal or greater than the smaller size of the image. """ - from torchvision.transforms.v2 import functional as func2 + import torch _init_default_device() + float_dtype = torch.float32 if _get_device() != torch.device("cuda") else torch.float16 + _check_blur_errors_and_warnings(radius, min(self.width, self.height), plural=False) - return Image(func2.gaussian_blur(self._image_tensor, [radius * 2 + 1, radius * 2 + 1])) + + kernel = torch.full( + (self._image_tensor.size(dim=-3), 1, radius * 2 + 1, radius * 2 + 1), + 1 / (radius * 2 + 1) ** 2, + dtype=float_dtype, + ) + tensor = torch.nn.functional.conv2d( + torch.nn.functional.pad( + self._image_tensor.to(float_dtype), + (radius, radius, radius, radius), + mode="replicate", + ), + kernel, + padding="valid", + groups=self._image_tensor.size(dim=-3), + ).to(torch.uint8) + return Image(tensor) def sharpen(self, factor: float) -> Image: """ @@ -734,7 +788,7 @@ def sharpen(self, factor: float) -> Image: torch.cat( [ func2.adjust_sharpness(self._image_tensor[0:3], factor * 1.0), - self._image_tensor[3].unsqueeze(dim=0), + self._image_tensor[3:4], ], ), ) @@ -759,7 +813,7 @@ def invert_colors(self) -> Image: if self.channel == 4: return Image( - torch.cat([func2.invert(self._image_tensor[0:3]), self._image_tensor[3].unsqueeze(dim=0)]), + torch.cat([func2.invert(self._image_tensor[0:3]), self._image_tensor[3:4]]), ) else: return Image(func2.invert(self._image_tensor)) @@ -813,20 +867,19 @@ def find_edges(self) -> Image: _init_default_device() - edges_tensor = torch.clamp( - torch.nn.functional.conv2d( - self.convert_to_grayscale()._image_tensor.float()[0].unsqueeze(dim=0), - Image._filter_edges_kernel(), - padding="same", - ).squeeze(dim=1), - 0, - 255, - ).to(torch.uint8) + edges_tensor_float16 = torch.nn.functional.conv2d( + self.convert_to_grayscale()._image_tensor.to(torch.float16)[0:1], + Image._filter_edges_kernel(), + padding="same", + ) + torch.clamp(edges_tensor_float16, 0, 255, out=edges_tensor_float16) + if self.channel == 1: + return Image(edges_tensor_float16.to(torch.uint8)) + edges_tensor = edges_tensor_float16.to(torch.uint8) + del edges_tensor_float16 if self.channel == 3: return Image(edges_tensor.repeat(3, 1, 1)) - elif self.channel == 4: + else: # self.channel == 4 return Image( - torch.cat([edges_tensor.repeat(3, 1, 1), self._image_tensor[3].unsqueeze(dim=0)]), + torch.cat([edges_tensor.repeat(3, 1, 1), self._image_tensor[3:4]]), ) - else: - return Image(edges_tensor) diff --git a/src/safeds/data/image/containers/_multi_size_image_list.py b/src/safeds/data/image/containers/_multi_size_image_list.py index f919776e9..186d6b79a 100644 --- a/src/safeds/data/image/containers/_multi_size_image_list.py +++ b/src/safeds/data/image/containers/_multi_size_image_list.py @@ -9,7 +9,9 @@ from safeds._utils import _structural_hash from safeds.data.image._utils._image_transformation_error_and_warning_checks import ( _check_blur_errors_and_warnings, + _check_crop_errors, _check_remove_images_with_size_errors, + _check_resize_errors, ) from safeds.data.image.containers import Image, ImageList from safeds.exceptions import ( @@ -374,7 +376,7 @@ def add_images(self, images: list[Image] | ImageList) -> ImageList: key=sorted(range(len(new_indices)), key=old_indices.__getitem__).__getitem__, ) ] - fixed_ims._calc_new_indices_to_tensor_positions() + fixed_ims._indices_to_tensor_positions = fixed_ims._calc_new_indices_to_tensor_positions() image_list._image_list_dict[size] = fixed_ims else: image_list._image_list_dict[size] = _SingleSizeImageList._create_image_list( @@ -435,9 +437,13 @@ def remove_images_with_size(self, width: int, height: int) -> ImageList: if (width, height) not in self._image_list_dict: return self if len(self._image_list_dict) == 2: - single_size_image_list = self._image_list_dict[ - next(iter([key for key in list(self._image_list_dict.keys()) if key != (width, height)])) - ]._as_single_size_image_list() + single_size_image_list = ( + self._image_list_dict[ + next(iter([key for key in list(self._image_list_dict.keys()) if key != (width, height)])) + ] + ._clone() + ._as_single_size_image_list() + ) single_size_image_list._tensor_positions_to_indices = torch.sort( torch.Tensor(single_size_image_list._tensor_positions_to_indices), )[1].tolist() @@ -506,14 +512,18 @@ def resize(self, new_width: int, new_height: int) -> ImageList: from safeds.data.image.containers._single_size_image_list import _SingleSizeImageList - image_list_tensors = [] + _check_resize_errors(new_width, new_height) + image_list_indices = [] + image_list = _SingleSizeImageList() + image_list._tensor = torch.empty(len(self), self.channel, new_height, new_width, dtype=torch.uint8) + current_start_index = 0 for image_list_original in self._image_list_dict.values(): image_list_new = image_list_original.resize(new_width, new_height)._as_single_size_image_list() - image_list_tensors.append(image_list_new._tensor) + end = current_start_index + len(image_list_original) + image_list._tensor[current_start_index:end] = image_list_new._tensor image_list_indices += image_list_new._tensor_positions_to_indices - image_list = _SingleSizeImageList() - image_list._tensor = torch.cat(image_list_tensors, dim=0) + current_start_index = end image_list._tensor_positions_to_indices = image_list_indices image_list._indices_to_tensor_positions = image_list._calc_new_indices_to_tensor_positions() return image_list @@ -531,14 +541,18 @@ def crop(self, x: int, y: int, width: int, height: int) -> ImageList: from safeds.data.image.containers._single_size_image_list import _SingleSizeImageList - image_list_tensors = [] + _check_crop_errors(x, y, width, height) + image_list_indices = [] + image_list = _SingleSizeImageList() + image_list._tensor = torch.empty(len(self), self.channel, width, height, dtype=torch.uint8) + current_start_index = 0 for image_list_original in self._image_list_dict.values(): image_list_new = image_list_original.crop(x, y, width, height)._as_single_size_image_list() - image_list_tensors.append(image_list_new._tensor) + end = current_start_index + len(image_list_original) + image_list._tensor[current_start_index:end] = image_list_new._tensor image_list_indices += image_list_new._tensor_positions_to_indices - image_list = _SingleSizeImageList() - image_list._tensor = torch.cat(image_list_tensors, dim=0) + current_start_index = end image_list._tensor_positions_to_indices = image_list_indices image_list._indices_to_tensor_positions = image_list._calc_new_indices_to_tensor_positions() return image_list diff --git a/src/safeds/data/image/containers/_single_size_image_list.py b/src/safeds/data/image/containers/_single_size_image_list.py index 6081acd24..459e82f28 100644 --- a/src/safeds/data/image/containers/_single_size_image_list.py +++ b/src/safeds/data/image/containers/_single_size_image_list.py @@ -1,6 +1,7 @@ from __future__ import annotations import copy +import math import random import sys from pathlib import Path @@ -14,7 +15,8 @@ _check_adjust_color_balance_errors_and_warnings, _check_adjust_contrast_errors_and_warnings, _check_blur_errors_and_warnings, - _check_crop_errors_and_warnings, + _check_crop_errors, + _check_crop_warnings, _check_remove_images_with_size_errors, _check_resize_errors, _check_sharpen_errors_and_warnings, @@ -132,34 +134,36 @@ def _create_image_list(images: list[Tensor], indices: list[int]) -> ImageList: return _EmptyImageList() image_list = _SingleSizeImageList() - images_ready_to_concat: list[tuple[Tensor, int]] = [] - images_with_correct_channel: list[tuple[Tensor, int]] = [] - images_with_less_channels: list[tuple[Tensor, int]] = [] + image_list._tensor_positions_to_indices = [] + images_with_channels: dict[int, list[Tensor]] = {} + indices_with_channels: dict[int, list[int]] = {} max_channel = 0 for image, index in zip(images, indices, strict=False): - if max_channel < image.size(dim=-3): # all images have to increase their channel - images_with_less_channels += images_with_correct_channel - images_with_correct_channel = [(image, index)] - max_channel = image.size(dim=-3) - elif max_channel > image.size(dim=-3): # current image has to increase its channel - images_with_less_channels.append((image, index)) - else: # current image has same channel as max_channel - images_with_correct_channel.append((image, index)) - for image, index in images_with_correct_channel: - images_ready_to_concat.append((image.unsqueeze(dim=0), index)) - for image, index in images_with_less_channels: - if max_channel == 3: # image channel 1 and max channel 3 - image_to_append = torch.cat([image, image, image], dim=0) - elif image.size(dim=0) == 1: # image channel 1 and max channel 4 - image_to_append = torch.cat( - [image, image, image, torch.full(image.size(), 255, device=image.device)], - dim=0, + current_channel = image.size(dim=-3) + if max_channel < current_channel: + max_channel = current_channel + if current_channel not in images_with_channels: + images_with_channels[current_channel] = [image] + indices_with_channels[current_channel] = [index] + else: + images_with_channels[current_channel].append(image) + indices_with_channels[current_channel].append(index) + + height = images[0].size(dim=-2) + width = images[0].size(dim=-1) + image_list._tensor = torch.empty(len(images), max_channel, height, width, dtype=torch.uint8) + current_start_index = 0 + for current_channel, ims in images_with_channels.items(): + for index, image in enumerate(ims): + image_list._tensor[index + current_start_index, 0 : max(current_channel, min(max_channel, 3))] = image + if max_channel == 4 and current_channel != 4: + torch.full( + (len(ims), 1, height, width), + 255, + out=image_list._tensor[current_start_index : current_start_index + len(ims), 3:4], ) - else: # image channel 3 and max channel 4 - image_to_append = torch.cat([image, torch.full(image[0:1].size(), 255, device=image.device)], dim=0) - images_ready_to_concat.append((image_to_append.unsqueeze(dim=0), index)) - image_list._tensor = torch.cat([image for image, index in images_ready_to_concat]) - image_list._tensor_positions_to_indices = [index for image, index in images_ready_to_concat] + current_start_index += len(ims) + image_list._tensor_positions_to_indices += indices_with_channels[current_channel] image_list._indices_to_tensor_positions = image_list._calc_new_indices_to_tensor_positions() return image_list @@ -496,25 +500,25 @@ def _add_image_tensor(self, image_tensor: Tensor, index: int) -> ImageList: ): image_list_single: _SingleSizeImageList = self._clone_without_tensor() if image_tensor.size(dim=0) > self.channel: - tensor = self.change_channel(image_tensor.size(dim=0))._as_single_size_image_list()._tensor + tensor = _SingleSizeImageList._change_channel_of_tensor(self._tensor, image_tensor.size(dim=0)) else: tensor = self._tensor if image_tensor.size(dim=0) < tensor.size(dim=1): - if tensor.size(dim=1) == 3: + if tensor.size(dim=1) == 3: # image_tensor channel == 1 image_list_single._tensor = torch.cat( - [tensor, torch.cat([image_tensor, image_tensor, image_tensor], dim=0).unsqueeze(dim=0)], + [tensor, torch.stack([image_tensor, image_tensor, image_tensor], dim=1)], ) - elif image_tensor.size(dim=0) == 1: + elif image_tensor.size(dim=0) == 1: # tensor channel == 4 image_list_single._tensor = torch.cat( [ tensor, - torch.cat( + torch.stack( [image_tensor, image_tensor, image_tensor, torch.full(image_tensor.size(), 255)], - dim=0, - ).unsqueeze(dim=0), + dim=1, + ), ], ) - else: + else: # image_tensor channel == 3; tensor channel == 4 image_list_single._tensor = torch.cat( [ tensor, @@ -557,71 +561,279 @@ def add_images(self, images: list[Image] | ImageList) -> ImageList: if isinstance(images, _EmptyImageList) or isinstance(images, list) and len(images) == 0: return self - first_new_index = max(self._tensor_positions_to_indices) + 1 + next_index = max(self._tensor_positions_to_indices) + 1 + + max_channel = self.channel + self_size = (self.widths[0], self.heights[0]) + if isinstance(images, list): - images = ImageList.from_images(images) - if isinstance(images, _MultiSizeImageList): - image_list_multi: _MultiSizeImageList = _MultiSizeImageList() - image_list_multi._image_list_dict[(self.widths[0], self.heights[0])] = self - for index in self._tensor_positions_to_indices: - image_list_multi._indices_to_image_size_dict[index] = (self.widths[0], self.heights[0]) - return image_list_multi.add_images(images) - else: - images_as_single_size_image_list: _SingleSizeImageList = images._as_single_size_image_list() - new_indices = [ - index + first_new_index for index in images_as_single_size_image_list._tensor_positions_to_indices - ] - if ( - images_as_single_size_image_list.widths[0] == self.widths[0] - and images_as_single_size_image_list.heights[0] == self.heights[0] - ): - image_list_single: _SingleSizeImageList = self._clone_without_tensor()._as_single_size_image_list() - image_list_single._tensor_positions_to_indices += new_indices - if self.channel > images_as_single_size_image_list.channel: - image_list_single._tensor = torch.cat( - [ + new_image_lists: dict[tuple[int, int], ImageList] = {} + + images_with_sizes_with_channel: dict[tuple[int, int], dict[int, list[Image]]] = {} + images_with_sizes_count: dict[tuple[int, int], int] = {} + indices_with_sizes_with_channel: dict[tuple[int, int], dict[int, list[int]]] = {} + for image in images: + current_size = (image.width, image.height) + current_channel = image.channel + if max_channel < current_channel: + max_channel = current_channel + if current_size not in images_with_sizes_with_channel: + images_with_sizes_with_channel[current_size] = {} + indices_with_sizes_with_channel[current_size] = {} + images_with_sizes_count[current_size] = 1 + else: + images_with_sizes_count[current_size] += 1 + if current_channel not in images_with_sizes_with_channel[current_size]: + images_with_sizes_with_channel[current_size][current_channel] = [image] + indices_with_sizes_with_channel[current_size][current_channel] = [next_index] + else: + images_with_sizes_with_channel[current_size][current_channel].append(image) + indices_with_sizes_with_channel[current_size][current_channel].append(next_index) + next_index += 1 + if self_size not in images_with_sizes_with_channel: + if self.channel != max_channel: + new_image_lists[self_size] = self.change_channel(max_channel) + else: + new_image_lists[self_size] = self + for size in images_with_sizes_with_channel: + if size == self_size: + new_tensor = torch.empty( + len(self) + images_with_sizes_count[size], + max_channel, + size[1], + size[0], + dtype=torch.uint8, + ) + new_indices = self._tensor_positions_to_indices + if self.channel != max_channel: + new_tensor[0 : len(self)] = _SingleSizeImageList._change_channel_of_tensor( self._tensor, - _SingleSizeImageList._change_channel_of_tensor( - images_as_single_size_image_list._tensor, - self.channel, - ), - ], + max_channel, + ) + else: + new_tensor[0 : len(self)] = self._tensor + current_index = len(self) + for channel in images_with_sizes_with_channel[size]: + new_indices += indices_with_sizes_with_channel[size][channel] + number_in_current_channel = len(images_with_sizes_with_channel[size][channel]) + end_index = current_index + number_in_current_channel + if channel < max_channel: + if channel == 3: + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 0:3], + ) + else: # channel == 1 + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 0:1], + ) + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 1:2], + ) + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 2:3], + ) + if max_channel == 4: + torch.full( + (number_in_current_channel, 1, size[1], size[0]), + 255, + dtype=torch.uint8, + out=new_tensor[current_index:end_index, 3:4], + ) + else: + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, :], + ) + current_index = end_index + new_image_list = _SingleSizeImageList() + new_image_list._tensor = new_tensor + new_image_list._tensor_positions_to_indices = new_indices + new_image_list._indices_to_tensor_positions = new_image_list._calc_new_indices_to_tensor_positions() + new_image_lists[size] = new_image_list + else: + new_tensor = torch.empty( + images_with_sizes_count[size], + max_channel, + size[1], + size[0], + dtype=torch.uint8, ) - elif self.channel < images_as_single_size_image_list.channel: - image_list_single._tensor = torch.cat( + new_indices = [] + current_index = 0 + for channel in images_with_sizes_with_channel[size]: + new_indices += indices_with_sizes_with_channel[size][channel] + number_in_current_channel = len(images_with_sizes_with_channel[size][channel]) + end_index = current_index + number_in_current_channel + if channel < max_channel: + if channel == 3: + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 0:3], + ) + else: # channel == 1 + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 0:1], + ) + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 1:2], + ) + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, 2:3], + ) + if max_channel == 4: + torch.full( + (number_in_current_channel, 1, size[1], size[0]), + 255, + dtype=torch.uint8, + out=new_tensor[current_index:end_index, 3:4], + ) + else: + torch.stack( + [img._image_tensor for img in images_with_sizes_with_channel[size][channel]], + dim=0, + out=new_tensor[current_index:end_index, :], + ) + current_index = end_index + new_image_list = _SingleSizeImageList() + new_image_list._tensor = new_tensor + new_image_list._tensor_positions_to_indices = new_indices + new_image_list._indices_to_tensor_positions = new_image_list._calc_new_indices_to_tensor_positions() + new_image_lists[size] = new_image_list + if len(new_image_lists) == 1: + return new_image_lists[next(iter(new_image_lists))] + else: + multi_image_list = _MultiSizeImageList() + multi_image_list._image_list_dict = new_image_lists + multi_image_list._indices_to_image_size_dict = {} + for size, im_list in new_image_lists.items(): + for index in im_list._as_single_size_image_list()._tensor_positions_to_indices: + multi_image_list._indices_to_image_size_dict[index] = size + return multi_image_list + else: # images is of type ImageList + max_channel = max(max_channel, images.channel) + index_offset = max(self._tensor_positions_to_indices) + if isinstance(images, _SingleSizeImageList): + return _SingleSizeImageList._combine_two_single_size_image_lists(self, images) + else: # images is of type _MultiSizeImageList + images = images._as_multi_size_image_list() + multi_image_list = _MultiSizeImageList() + multi_image_list._image_list_dict = {} + multi_image_list._indices_to_image_size_dict = {} + if self_size in images._image_list_dict: + new_self_im_list = _SingleSizeImageList._combine_two_single_size_image_lists( + self, + images._image_list_dict[self_size]._as_single_size_image_list(), + ) + elif self.channel != max_channel: + new_self_im_list = self.change_channel(max_channel) + else: + new_self_im_list = self + multi_image_list._image_list_dict[self_size] = new_self_im_list + for index in new_self_im_list._as_single_size_image_list()._tensor_positions_to_indices: + multi_image_list._indices_to_image_size_dict[index] = self_size + for im_size, im_list in images._image_list_dict.items(): + if im_size == self_size: + continue + if im_list.channel != max_channel: + new_im_list = im_list.change_channel(max_channel)._as_single_size_image_list() + else: + new_im_list = im_list._as_single_size_image_list() + new_im_list._tensor_positions_to_indices = [ + old_index + index_offset + for old_index in im_list._as_single_size_image_list()._tensor_positions_to_indices + ] + new_im_list._indices_to_tensor_positions = new_im_list._calc_new_indices_to_tensor_positions() + multi_image_list._image_list_dict[im_size] = new_im_list + for new_index in new_im_list._tensor_positions_to_indices: + multi_image_list._indices_to_image_size_dict[new_index] = im_size + return multi_image_list + + @staticmethod + def _combine_two_single_size_image_lists( + image_list_1: _SingleSizeImageList, + image_list_2: _SingleSizeImageList, + ) -> ImageList: + import torch + + from safeds.data.image.containers._multi_size_image_list import _MultiSizeImageList + + max_channel = max(image_list_1.channel, image_list_2.channel) + im1_size = (image_list_1.widths[0], image_list_1.heights[0]) + im2_size = (image_list_2.widths[0], image_list_2.heights[0]) + index_offset = max(image_list_1._tensor_positions_to_indices) + 1 + + if im1_size == im2_size: + if image_list_2.channel == image_list_1.channel: + new_image_list = _SingleSizeImageList() + new_image_list._tensor = torch.cat([image_list_1._tensor, image_list_2._tensor], dim=0) + new_image_list._tensor_positions_to_indices = image_list_1._tensor_positions_to_indices + [ + old_index + index_offset for old_index in image_list_2._tensor_positions_to_indices + ] + new_image_list._indices_to_tensor_positions = new_image_list._calc_new_indices_to_tensor_positions() + return new_image_list + else: + new_image_list = _SingleSizeImageList() + if image_list_2.channel < max_channel: + new_image_list._tensor = torch.cat( [ - _SingleSizeImageList._change_channel_of_tensor( - self._tensor, - images_as_single_size_image_list.channel, - ), - images_as_single_size_image_list._tensor, + image_list_1._tensor, + _SingleSizeImageList._change_channel_of_tensor(image_list_2._tensor, max_channel), ], + dim=0, ) else: - image_list_single._tensor = torch.cat([self._tensor, images_as_single_size_image_list._tensor]) - image_list_single._indices_to_tensor_positions = ( - image_list_single._calc_new_indices_to_tensor_positions() - ) - return image_list_single - else: - image_list_multi = _MultiSizeImageList() - image_list_multi._image_list_dict[(self.widths[0], self.heights[0])] = self - image_list_multi._image_list_dict[ - (images_as_single_size_image_list.widths[0], images_as_single_size_image_list.heights[0]) - ] = images_as_single_size_image_list - for index in self._tensor_positions_to_indices: - image_list_multi._indices_to_image_size_dict[index] = (self.widths[0], self.heights[0]) - for index in images_as_single_size_image_list._tensor_positions_to_indices: - image_list_multi._indices_to_image_size_dict[index + first_new_index] = ( - images_as_single_size_image_list.widths[0], - images_as_single_size_image_list.heights[0], + new_image_list._tensor = torch.cat( + [ + _SingleSizeImageList._change_channel_of_tensor(image_list_1._tensor, max_channel), + image_list_2._tensor, + ], + dim=0, ) - - if images_as_single_size_image_list.channel != self.channel: - image_list_multi = image_list_multi.change_channel( - max(images_as_single_size_image_list.channel, self.channel), - )._as_multi_size_image_list() - return image_list_multi + new_image_list._tensor_positions_to_indices = image_list_1._tensor_positions_to_indices + [ + old_index + index_offset for old_index in image_list_2._tensor_positions_to_indices + ] + new_image_list._indices_to_tensor_positions = new_image_list._calc_new_indices_to_tensor_positions() + return new_image_list + else: + multi_image_list = _MultiSizeImageList() + multi_image_list._image_list_dict = {} + im_l_2 = _SingleSizeImageList() + if image_list_2.channel == image_list_1.channel: + multi_image_list._image_list_dict[im1_size] = image_list_1 + im_l_2._tensor = image_list_2._tensor + elif image_list_2.channel < image_list_1.channel: + multi_image_list._image_list_dict[im1_size] = image_list_1 + im_l_2._tensor = _SingleSizeImageList._change_channel_of_tensor(image_list_2._tensor, max_channel) + else: + multi_image_list._image_list_dict[im1_size] = image_list_1.change_channel(max_channel) + im_l_2._tensor = image_list_2._tensor + im_l_2._tensor_positions_to_indices = [ + old_index + index_offset for old_index in image_list_2._tensor_positions_to_indices + ] + im_l_2._indices_to_tensor_positions = im_l_2._calc_new_indices_to_tensor_positions() + multi_image_list._image_list_dict[im2_size] = im_l_2 + multi_image_list._indices_to_image_size_dict = {} + for index in image_list_1._tensor_positions_to_indices: + multi_image_list._indices_to_image_size_dict[index] = im1_size + for index in image_list_2._tensor_positions_to_indices: + multi_image_list._indices_to_image_size_dict[index + index_offset] = im2_size + return multi_image_list def remove_image_by_index(self, index: int | list[int]) -> ImageList: if isinstance(index, int): @@ -653,7 +865,7 @@ def _remove_image_by_index_ignore_invalid(self, index: int | list[int]) -> Image image_list = _SingleSizeImageList() image_list._tensor = self._tensor[ [i for i, v in enumerate(self._tensor_positions_to_indices) if v not in index] - ].clone() + ] image_list._tensor_positions_to_indices = [ i - len([k for k in index if k < i]) for i in self._tensor_positions_to_indices if i not in index ] @@ -676,6 +888,8 @@ def remove_duplicate_images(self) -> ImageList: dim=0, return_inverse=True, ) # Works somehow faster on cpu + if tensor_cpu_unique.size(dim=-4) == self._tensor.size(dim=-4): # no duplicates + return self image_list._tensor = tensor_cpu_unique.to(self._tensor.device) indices, indices_to_remove = [], [] offset_indices: list[int] = [] @@ -717,6 +931,8 @@ def resize(self, new_width: int, new_height: int) -> ImageList: return image_list def convert_to_grayscale(self) -> ImageList: + if self.channel == 1: + return self image_list = self._clone_without_tensor() image_list._tensor = _SingleSizeImageList._convert_tensor_to_grayscale(self._tensor) return image_list @@ -728,20 +944,23 @@ def _convert_tensor_to_grayscale(tensor: Tensor) -> Tensor: _init_default_device() - if tensor.size(dim=-3) == 4: + if tensor.size(dim=-3) == 1: + return tensor + elif tensor.size(dim=-3) == 4: return torch.cat( - [func2.rgb_to_grayscale(tensor[:, 0:3], num_output_channels=3), tensor[:, 3].unsqueeze(dim=1)], + [func2.rgb_to_grayscale(tensor[:, 0:3], num_output_channels=3), tensor[:, 3:4]], dim=1, ) - else: - return func2.rgb_to_grayscale(tensor[:, 0:3], num_output_channels=tensor.size(dim=-3)) + else: # channel == 3 + return func2.rgb_to_grayscale(tensor[:, 0:3], num_output_channels=3) def crop(self, x: int, y: int, width: int, height: int) -> ImageList: from torchvision.transforms.v2 import functional as func2 _init_default_device() - _check_crop_errors_and_warnings(x, y, width, height, self.widths[0], self.heights[0], plural=True) + _check_crop_errors(x, y, width, height) + _check_crop_warnings(x, y, self.widths[0], self.heights[0], plural=True) image_list = self._clone_without_tensor() image_list._tensor = func2.crop(self._tensor, x, y, height, width) return image_list @@ -766,19 +985,26 @@ def flip_horizontally(self) -> ImageList: def adjust_brightness(self, factor: float) -> ImageList: import torch - from torchvision.transforms.v2 import functional as func2 _init_default_device() _check_adjust_brightness_errors_and_warnings(factor, plural=True) image_list = self._clone_without_tensor() - if self.channel == 4: - image_list._tensor = torch.cat( - [func2.adjust_brightness(self._tensor[:, 0:3], factor * 1.0), self._tensor[:, 3].unsqueeze(dim=1)], - dim=1, + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + channel = self._tensor.size(dim=-3) + if factor == 0: + torch.zeros( + (self._tensor.size(dim=-4), min(3, channel), self._tensor.size(dim=-2), self._tensor.size(dim=-1)), + dtype=torch.uint8, + out=image_list._tensor[:, 0 : min(self._tensor.size(dim=-3), 3)], ) else: - image_list._tensor = func2.adjust_brightness(self._tensor, factor * 1.0) + temp_tensor = self._tensor[:, 0 : min(channel, 3)] * torch.tensor([factor * 1.0], dtype=torch.float16) + torch.clamp(temp_tensor, 0, 255, out=temp_tensor) + image_list._tensor[:, 0 : min(channel, 3)] = temp_tensor[:, :] + if channel == 4: + image_list._tensor[:, 3] = self._tensor[:, 3] + return image_list def add_noise(self, standard_deviation: float) -> ImageList: @@ -788,44 +1014,100 @@ def add_noise(self, standard_deviation: float) -> ImageList: _check_add_noise_errors(standard_deviation) image_list = self._clone_without_tensor() - image_list._tensor = ( - self._tensor + torch.normal(0, standard_deviation, self._tensor.size()).to(_get_device()) * 255 - ) + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + float_tensor = torch.empty(self._tensor.size(), dtype=torch.float16) + torch.normal(0, standard_deviation, self._tensor.size(), out=float_tensor) + float_tensor *= 255 + float_tensor += self._tensor + torch.clamp(float_tensor, 0, 255, out=float_tensor) + image_list._tensor[:] = float_tensor[:] return image_list def adjust_contrast(self, factor: float) -> ImageList: import torch - from torchvision.transforms.v2 import functional as func2 _init_default_device() _check_adjust_contrast_errors_and_warnings(factor, plural=True) image_list = self._clone_without_tensor() - if self.channel == 4: - image_list._tensor = torch.cat( - [func2.adjust_contrast(self._tensor[:, 0:3], factor * 1.0), self._tensor[:, 3].unsqueeze(dim=1)], - dim=1, - ) - else: - image_list._tensor = func2.adjust_contrast(self._tensor, factor * 1.0) + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + + channel = self._tensor.size(dim=-3) + factor *= 1.0 + adjusted_factor = (1 - factor) / factor + + gray_tensor = _SingleSizeImageList._convert_tensor_to_grayscale(self._tensor[:, 0 : min(channel, 3)]) + mean = torch.mean(gray_tensor, dim=(-3, -2, -1), keepdim=True, dtype=torch.float16) + del gray_tensor + mean *= torch.tensor(adjusted_factor, dtype=torch.float16) + adjusted_tensor = mean.repeat(1, min(channel, 3), self._tensor.size(dim=-2), self._tensor.size(dim=-1)) + adjusted_tensor += self._tensor[:, 0 : min(channel, 3)] + adjusted_tensor *= factor + torch.clamp(adjusted_tensor, 0, 255, out=adjusted_tensor) + image_list._tensor[:, 0 : min(channel, 3)] = adjusted_tensor[:, :] + + if channel == 4: + image_list._tensor[:, 3] = self._tensor[:, 3] + return image_list def adjust_color_balance(self, factor: float) -> ImageList: + import torch + _check_adjust_color_balance_errors_and_warnings(factor, self.channel, plural=True) image_list = self._clone_without_tensor() - image_list._tensor = self.convert_to_grayscale()._as_single_size_image_list()._tensor * ( - 1.0 - factor * 1.0 - ) + self._tensor * (factor * 1.0) + factor *= 1.0 + if factor == 0: + image_list._tensor = _SingleSizeImageList._convert_tensor_to_grayscale(self._tensor) + else: + adjusted_factor = (1 - factor) / factor + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + adjusted_tensor = _SingleSizeImageList._convert_tensor_to_grayscale(self._tensor) * torch.tensor( + adjusted_factor, + dtype=torch.float16, + ) + adjusted_tensor += self._tensor + adjusted_tensor *= factor + torch.clamp(adjusted_tensor, 0, 255, out=adjusted_tensor) + image_list._tensor[:] = adjusted_tensor[:] return image_list def blur(self, radius: int) -> ImageList: - from torchvision.transforms.v2 import functional as func2 + import torch _init_default_device() + float_dtype = torch.float32 if _get_device() != torch.device("cuda") else torch.float16 + _check_blur_errors_and_warnings(radius, min(self.widths[0], self.heights[0]), plural=True) image_list = self._clone_without_tensor() - image_list._tensor = func2.gaussian_blur(self._tensor, [radius * 2 + 1, radius * 2 + 1]) + + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + + kernel = torch.full( + (self._tensor.size(dim=-3), 1, radius * 2 + 1, radius * 2 + 1), + 1 / (radius * 2 + 1) ** 2, + dtype=float_dtype, + ) + image_tensor_size = ( + self._tensor.size(dim=1) * (self._tensor.size(dim=2) + radius * 2) * (self._tensor.size(dim=3) + radius * 2) + ) + number_of_executions = math.ceil(self._tensor.size(dim=0) / (2**31 / image_tensor_size)) + number_of_images_per_execution = math.ceil(self._tensor.size(dim=0) / number_of_executions) + start = 0 + for i in range(number_of_executions): + end = min((i + 1) * number_of_images_per_execution, self._tensor.size(dim=0)) + 1 + image_list._tensor[start:end] = torch.nn.functional.conv2d( + torch.nn.functional.pad( + self._tensor[start:end].to(float_dtype), + (radius, radius, radius, radius), + mode="replicate", + ), + kernel, + padding="valid", + groups=self._tensor.size(dim=-3), + )[:] + start = end return image_list def sharpen(self, factor: float) -> ImageList: @@ -836,13 +1118,26 @@ def sharpen(self, factor: float) -> ImageList: _check_sharpen_errors_and_warnings(factor, plural=True) image_list = self._clone_without_tensor() + image_list._tensor = torch.empty(self._tensor.size(), dtype=torch.uint8) + + image_tensor_size = self._tensor.size(dim=1) * self._tensor.size(dim=2) * self._tensor.size(dim=3) + number_of_executions = math.ceil(self._tensor.size(dim=0) / (2**31 / image_tensor_size)) + number_of_images_per_execution = math.ceil(self._tensor.size(dim=0) / number_of_executions) + start = 0 + if self.channel == 4: - image_list._tensor = torch.cat( - [func2.adjust_sharpness(self._tensor[:, 0:3], factor * 1.0), self._tensor[:, 3].unsqueeze(dim=1)], - dim=1, - ) + for i in range(number_of_executions): + end = min((i + 1) * number_of_images_per_execution, self._tensor.size(dim=0)) + 1 + image_list._tensor[start:end] = torch.cat( + [func2.adjust_sharpness(self._tensor[start:end, 0:3], factor * 1.0), self._tensor[start:end, 3:4]], + dim=1, + ) + start = end else: - image_list._tensor = func2.adjust_sharpness(self._tensor, factor * 1.0) + for i in range(number_of_executions): + end = min((i + 1) * number_of_images_per_execution, self._tensor.size(dim=0)) + 1 + image_list._tensor[start:end] = func2.adjust_sharpness(self._tensor[start:end], factor * 1.0) + start = end return image_list def invert_colors(self) -> ImageList: @@ -854,7 +1149,7 @@ def invert_colors(self) -> ImageList: image_list = self._clone_without_tensor() if self.channel == 4: image_list._tensor = torch.cat( - [func2.invert(self._tensor[:, 0:3]), self._tensor[:, 3].unsqueeze(dim=1)], + [func2.invert(self._tensor[:, 0:3]), self._tensor[:, 3:4]], dim=1, ) else: @@ -885,23 +1180,25 @@ def find_edges(self) -> ImageList: _init_default_device() kernel = Image._filter_edges_kernel() - edges_tensor = torch.clamp( - torch.nn.functional.conv2d( - self.convert_to_grayscale()._as_single_size_image_list()._tensor.float()[:, 0].unsqueeze(dim=1), - kernel, - padding="same", - ), - 0, - 255, - ).to(torch.uint8) + image_list = self._clone_without_tensor() + + edges_tensor_float16 = torch.nn.functional.conv2d( + _SingleSizeImageList._convert_tensor_to_grayscale(self._tensor).to(torch.float16)[:, 0:1], + kernel, + padding="same", + ) + torch.clamp(edges_tensor_float16, 0, 255, out=edges_tensor_float16) + if self.channel == 1: + image_list._tensor = edges_tensor_float16.to(torch.uint8) + return image_list + edges_tensor = edges_tensor_float16.to(torch.uint8) + del edges_tensor_float16 if self.channel == 3: image_list._tensor = edges_tensor.repeat(1, 3, 1, 1) - elif self.channel == 4: + else: # self.channel == 4 image_list._tensor = torch.cat( - [edges_tensor.repeat(1, 3, 1, 1), self._tensor[:, 3].unsqueeze(dim=1)], + [edges_tensor.repeat(1, 3, 1, 1), self._tensor[:, 3:4]], dim=1, ) - else: - image_list._tensor = edges_tensor return image_list diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cpu].png index f53b0a950..79a7c4c9c 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cuda].png index f53b0a950..79a7c4c9c 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-minimum noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cpu].png index 1dd360e32..8257634bf 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cuda].png index 1dd360e32..a76595977 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cpu].png index 66b8714af..2e4276d05 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cuda].png index 66b8714af..f98baaef9 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-jpg-grayscale-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cpu].png index c4d2ce71b..c0a5f338f 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cuda].png index c4d2ce71b..c0a5f338f 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-minimum noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cpu].png index 32967f5fa..9577b1c9a 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cuda].png index 32967f5fa..2c3aaa79b 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cpu].png index b20e04f08..5d8a4da82 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cuda].png index b20e04f08..7109ad08d 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-1-channel-png-grayscale-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cpu].png index 30cb3df01..617a949c8 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cuda].png index 9ae749de6..6aa8f09df 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cpu].png index e0f51f631..d1f613fad 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cuda].png index e0f51f631..04f29c3f7 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-plane-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cpu].png index ce7dc5542..683d42e6d 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cuda].png index ce7dc5542..2ce1b6485 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-very noisy-cuda].png index abac9c634..19b3f6a1e 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-jpg-white_square-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cpu].png index ce7dc5542..683d42e6d 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cuda].png index ce7dc5542..2ce1b6485 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-very noisy-cuda].png index abac9c634..19b3f6a1e 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-3-channel-png-white_square-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cpu].png index 8f111fe17..8d5456d9a 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cuda].png index 0ca88c029..07756a179 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cpu].png index e5e42ce2c..477a70da1 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cuda].png index e5e42ce2c..0b821c743 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[opaque-4-channel-png-plane-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cpu].png index b86106b61..dd84d2370 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cuda].png index b86106b61..2837c7fea 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-some noise-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cpu].png index e94185872..99f3d4b57 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cuda].png index e94185872..444851a82 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAddNoise.test_should_add_noise[transparent-4-channel-png-rgba-very noisy-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cpu].png index fd0f65f87..efca68b58 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cuda].png index fd0f65f87..efca68b58 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-remove color-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cpu].png deleted file mode 100644 index 94381b429..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cpu].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cuda].png deleted file mode 100644 index 94381b429..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-add color-cuda].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cpu].png index 94381b429..b9028365b 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cuda].png index 94381b429..b9028365b 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-white_square-remove color-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cpu].png deleted file mode 100644 index 94381b429..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cpu].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cuda].png deleted file mode 100644 index 94381b429..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-add color-cuda].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cpu].png index 94381b429..b9028365b 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cuda].png index 94381b429..b9028365b 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-png-white_square-remove color-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cpu].png index 99168d9f7..c11b8b9a7 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cuda].png index 99168d9f7..c11b8b9a7 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-remove color-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cpu].png deleted file mode 100644 index 6da78e96e..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cpu].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cuda].png deleted file mode 100644 index 6da78e96e..000000000 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-add color-cuda].png and /dev/null differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cpu].png index 5d847799e..e49100d86 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cuda].png index 5d847799e..e49100d86 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[transparent-4-channel-png-rgba-remove color-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-add color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-3-channel-jpg-plane-cpu].png similarity index 100% rename from tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-add color-cpu].png rename to tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-3-channel-jpg-plane-cpu].png diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-add color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-3-channel-jpg-plane-cuda].png similarity index 100% rename from tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-3-channel-jpg-plane-add color-cuda].png rename to tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-3-channel-jpg-plane-cuda].png diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-add color-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-4-channel-png-plane-cpu].png similarity index 100% rename from tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-add color-cpu].png rename to tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-4-channel-png-plane-cpu].png diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-add color-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-4-channel-png-plane-cuda].png similarity index 100% rename from tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors[opaque-4-channel-png-plane-add color-cuda].png rename to tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustColor.test_should_adjust_colors_add_color[opaque-4-channel-png-plane-cuda].png diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cpu].png index 0b90605f5..e703c0c96 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cuda].png index 0b90605f5..e703c0c96 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-1-channel-jpg-grayscale-small factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cpu].png index 61a4cc708..110f886fe 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cuda].png index 61a4cc708..110f886fe 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-3-channel-jpg-plane-small factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cpu].png index 7919d06fc..a538582ca 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cuda].png index 7919d06fc..a538582ca 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestAdjustContrast.test_should_adjust_contrast[opaque-4-channel-png-plane-small factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cpu].png index 7cf5c2cf6..3cd48ed17 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cuda].png index 7cf5c2cf6..3cd48ed17 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-jpg-grayscale-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cpu].png index 1852f77cc..84aae7d13 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cuda].png index 1852f77cc..84aae7d13 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-1-channel-png-grayscale-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cpu].png index 42dae9ef3..4544b1b4d 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cuda].png index 42dae9ef3..4544b1b4d 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-3-channel-jpg-plane-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cpu].png index d00afad1f..c5e589cd2 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cuda].png index d00afad1f..c5e589cd2 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[opaque-4-channel-png-plane-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cpu].png index 6f196813d..3484508cf 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cuda].png index 6f196813d..3484508cf 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBlur.test_should_return_blurred_image[transparent-4-channel-png-rgba-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cpu].png new file mode 100644 index 000000000..f2ff9e033 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cuda].png new file mode 100644 index 000000000..f2ff9e033 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-jpg-grayscale-zero factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cpu].png new file mode 100644 index 000000000..f2ff9e033 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cuda].png new file mode 100644 index 000000000..f2ff9e033 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-1-channel-png-grayscale-zero factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cpu].png new file mode 100644 index 000000000..49b121419 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cuda].png new file mode 100644 index 000000000..49b121419 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-3-channel-jpg-plane-zero factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cpu].png new file mode 100644 index 000000000..8107aed35 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cuda].png new file mode 100644 index 000000000..8107aed35 Binary files /dev/null and b/tests/safeds/data/image/containers/__snapshots__/test_image/TestBrightness.test_should_adjust_brightness[opaque-4-channel-png-plane-zero factor-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cpu].png index 27798023e..73b3225fa 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cuda].png index 8f52164a2..5a30658df 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-all-images-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cpu].png index 62e13b82c..0bef1b7e2 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cuda].png index 64f1845d0..10cade7f2 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[some noise-planes-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cpu].png index aacc1a802..946c48bce 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cuda].png index bbb2517ae..ed291d534 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-all-images-cuda].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cpu].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cpu].png index eb136da83..e8e510cd4 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cpu].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cpu].png differ diff --git a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cuda].png b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cuda].png index 5e67a2173..d8eceb418 100644 Binary files a/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cuda].png and b/tests/safeds/data/image/containers/__snapshots__/test_image_list/TestTransforms.TestAddNoise.test_should_add_noise[very noisy-planes-cuda].png differ diff --git a/tests/safeds/data/image/containers/test_image.py b/tests/safeds/data/image/containers/test_image.py index ad3ae820b..d1f145046 100644 --- a/tests/safeds/data/image/containers/test_image.py +++ b/tests/safeds/data/image/containers/test_image.py @@ -612,7 +612,7 @@ def test_should_be_original(self, resource_path: str, device: Device) -> None: @pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids()) class TestBrightness: - @pytest.mark.parametrize("factor", [0.5, 10], ids=["small factor", "large factor"]) + @pytest.mark.parametrize("factor", [0, 0.5, 10], ids=["zero factor", "small factor", "large factor"]) @pytest.mark.parametrize( "resource_path", [plane_jpg_path, plane_png_path, grayscale_png_path, grayscale_jpg_path], @@ -762,7 +762,7 @@ def test_should_raise_negative_contrast(self, resource_path: str, device: Device @pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids()) class TestAdjustColor: - @pytest.mark.parametrize("factor", [2, 0.5, 0], ids=["add color", "remove color", "gray"]) + @pytest.mark.parametrize("factor", [0.5, 0], ids=["remove color", "gray"]) @pytest.mark.parametrize( "resource_path", [plane_jpg_path, plane_png_path, rgba_png_path, white_square_jpg_path, white_square_png_path], @@ -781,6 +781,23 @@ def test_should_adjust_colors( assert image != image_adjusted_color_balance assert image_adjusted_color_balance == snapshot_png_image + @pytest.mark.parametrize( + "resource_path", + [plane_jpg_path, plane_png_path], + ids=[plane_jpg_id, plane_png_id], + ) + def test_should_adjust_colors_add_color( + self, + resource_path: str, + snapshot_png_image: SnapshotAssertion, + device: Device, + ) -> None: + configure_test_with_device(device) + image = Image.from_file(resolve_resource_path(resource_path)) + image_adjusted_color_balance = image.adjust_color_balance(2) + assert image != image_adjusted_color_balance + assert image_adjusted_color_balance == snapshot_png_image + @pytest.mark.parametrize( "resource_path", images_all(), @@ -811,6 +828,17 @@ def test_should_not_adjust_colors_channel_1(self, resource_path: str, device: De image_adjusted_color_balance = image.adjust_color_balance(0.5) assert image == image_adjusted_color_balance + @pytest.mark.parametrize( + "resource_path", + [rgba_png_path, white_square_png_path, white_square_jpg_path], + ids=[rgba_png_id, white_square_png_id, white_square_jpg_id], + ) + def test_should_not_adjust_colors_value_255(self, resource_path: str, device: Device) -> None: + configure_test_with_device(device) + image = Image.from_file(resolve_resource_path(resource_path)) + image_adjusted_color_balance = image.adjust_color_balance(2) + assert image == image_adjusted_color_balance + @pytest.mark.parametrize( "resource_path", images_all(), diff --git a/tests/safeds/data/image/containers/test_image_list.py b/tests/safeds/data/image/containers/test_image_list.py index 06bf5fdaa..d8da7f6b6 100644 --- a/tests/safeds/data/image/containers/test_image_list.py +++ b/tests/safeds/data/image/containers/test_image_list.py @@ -893,6 +893,7 @@ class TestTransformsEqualImageTransforms: ("crop", [0, 0, 100, 100]), ("flip_vertically", None), ("flip_horizontally", None), + ("adjust_brightness", [0]), ("adjust_brightness", [0.5]), ("adjust_brightness", [10]), ("adjust_contrast", [0.75]), @@ -917,6 +918,7 @@ class TestTransformsEqualImageTransforms: "crop-(0, 0, 100, 100)", "flip_vertically", "flip_horizontally", + "adjust_brightness-zero factor", "adjust_brightness-small factor", "adjust_brightness-large factor", "adjust_contrast-small factor", @@ -1629,6 +1631,7 @@ def test_remove_image_by_index(self, device: Device) -> None: ("crop", [0, 0, 100, 100]), ("flip_vertically", None), ("flip_horizontally", None), + ("adjust_brightness", [0]), ("adjust_brightness", [0.5]), ("adjust_brightness", [10]), ("add_noise", [10]), @@ -1660,6 +1663,7 @@ def test_remove_image_by_index(self, device: Device) -> None: "crop-(0, 0, 100, 100)", "flip_vertically", "flip_horizontally", + "adjust_brightness-zero factor", "adjust_brightness-small factor", "adjust_brightness-large factor", "add_noise",