Skip to content

Commit

Permalink
feat: rename flip_vertically and flip_horizontally methods (#946)
Browse files Browse the repository at this point in the history
Closes #945

### Summary of Changes

In `Image` and `ImageList`, rename

* `flip_vertically` to `flip_top_and_bottom`
* `flip_horizontally` to `flip_left_and_right`.

This makes it completely clear how the image is transformed.

---------

Co-authored-by: megalinter-bot <[email protected]>
  • Loading branch information
lars-reimann and megalinter-bot authored Nov 20, 2024
1 parent c91c2c7 commit a3607d7
Show file tree
Hide file tree
Showing 29 changed files with 150 additions and 570 deletions.
337 changes: 61 additions & 276 deletions docs/tutorials/image_list_processing.ipynb

Large diffs are not rendered by default.

286 changes: 43 additions & 243 deletions docs/tutorials/image_processing.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/safeds/data/image/containers/_empty_image_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
_check_crop_errors(x, y, width, height)
return _EmptyImageList()

def flip_vertically(self) -> ImageList:
def flip_top_and_bottom(self) -> ImageList:
_EmptyImageList._warn_empty_image_list()
return _EmptyImageList()

def flip_horizontally(self) -> ImageList:
def flip_left_and_right(self) -> ImageList:
_EmptyImageList._warn_empty_image_list()
return _EmptyImageList()

Expand Down
8 changes: 4 additions & 4 deletions src/safeds/data/image/containers/_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,9 @@ def crop(self, x: int, y: int, width: int, height: int) -> Image:
_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:
def flip_top_and_bottom(self) -> Image:
"""
Return a new `Image` that is flipped vertically (horizontal axis, flips up-down and vice versa).
Return a new `Image` where top and bottom are flipped along a horizontal axis.
The original image is not modified.
Expand All @@ -519,9 +519,9 @@ def flip_vertically(self) -> Image:

return Image(func2.vertical_flip(self._image_tensor))

def flip_horizontally(self) -> Image:
def flip_left_and_right(self) -> Image:
"""
Return a new `Image` that is flipped horizontally (vertical axis, flips left-right and vice versa).
Return a new `Image` where left and right sides are flipped along a vertical axis.
The original image is not modified.
Expand Down
11 changes: 5 additions & 6 deletions src/safeds/data/image/containers/_image_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ def from_files(
im = pil_image_open(filename)
im_channel = len(im.getbands())
im_size = (im.width, im.height)
if im_channel > max_channel:
max_channel = im_channel
max_channel = max(im_channel, max_channel)
if im_size not in image_sizes:
image_sizes[im_size] = {im_channel: [filename]}
image_indices[im_size] = {im_channel: [i]}
Expand Down Expand Up @@ -917,9 +916,9 @@ def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
"""

@abstractmethod
def flip_vertically(self) -> ImageList:
def flip_top_and_bottom(self) -> ImageList:
"""
Return a new `ImageList` with all images flipped vertically (horizontal axis, flips up-down and vice versa).
Return a new `ImageList` where top and bottom of all images are flipped along a horizontal axis.
The original image list is not modified.
Expand All @@ -930,9 +929,9 @@ def flip_vertically(self) -> ImageList:
"""

@abstractmethod
def flip_horizontally(self) -> ImageList:
def flip_left_and_right(self) -> ImageList:
"""
Return a new `ImageList` with all images flipped horizontally (vertical axis, flips left-right and vice versa).
Return a new `ImageList` where left and right sides of all images are flipped along a vertical axis.
The original image list is not modified.
Expand Down
14 changes: 6 additions & 8 deletions src/safeds/data/image/containers/_multi_size_image_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,7 @@ def add_images(self, images: list[Image] | ImageList) -> ImageList:
+ new_indices,
)
elif isinstance(ims, _SingleSizeImageList):
if smallest_channel > ims.channel:
smallest_channel = ims.channel
smallest_channel = min(smallest_channel, ims.channel)
fixed_ims = ims
old_indices = list(fixed_ims._indices_to_tensor_positions.items())
fixed_ims._tensor_positions_to_indices = [
Expand All @@ -383,8 +382,7 @@ def add_images(self, images: list[Image] | ImageList) -> ImageList:
[im._image_tensor for im in ims],
new_indices,
)
if smallest_channel > image_list._image_list_dict[size].channel:
smallest_channel = image_list._image_list_dict[size].channel
smallest_channel = min(smallest_channel, image_list._image_list_dict[size].channel)
for i in new_indices:
image_list._indices_to_image_size_dict[i] = size
max_channel = max(max_channel, image_list._image_list_dict[size].channel)
Expand Down Expand Up @@ -557,16 +555,16 @@ def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
image_list._indices_to_tensor_positions = image_list._calc_new_indices_to_tensor_positions()
return image_list

def flip_vertically(self) -> ImageList:
def flip_top_and_bottom(self) -> ImageList:
image_list = self._clone_without_image_dict()
for image_list_key, image_list_original in self._image_list_dict.items():
image_list._image_list_dict[image_list_key] = image_list_original.flip_vertically()
image_list._image_list_dict[image_list_key] = image_list_original.flip_top_and_bottom()
return image_list

def flip_horizontally(self) -> ImageList:
def flip_left_and_right(self) -> ImageList:
image_list = self._clone_without_image_dict()
for image_list_key, image_list_original in self._image_list_dict.items():
image_list._image_list_dict[image_list_key] = image_list_original.flip_horizontally()
image_list._image_list_dict[image_list_key] = image_list_original.flip_left_and_right()
return image_list

def adjust_brightness(self, factor: float) -> ImageList:
Expand Down
10 changes: 4 additions & 6 deletions src/safeds/data/image/containers/_single_size_image_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ def _create_image_list(images: list[Tensor], indices: list[int]) -> ImageList:
max_channel = 0
for image, index in zip(images, indices, strict=False):
current_channel = image.size(dim=-3)
if max_channel < current_channel:
max_channel = current_channel
max_channel = max(max_channel, current_channel)
if current_channel not in images_with_channels:
images_with_channels[current_channel] = [image]
indices_with_channels[current_channel] = [index]
Expand Down Expand Up @@ -575,8 +574,7 @@ def add_images(self, images: list[Image] | ImageList) -> ImageList:
for image in images:
current_size = (image.width, image.height)
current_channel = image.channel
if max_channel < current_channel:
max_channel = current_channel
max_channel = max(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] = {}
Expand Down Expand Up @@ -965,7 +963,7 @@ def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
image_list._tensor = func2.crop(self._tensor, x, y, height, width)
return image_list

def flip_vertically(self) -> ImageList:
def flip_top_and_bottom(self) -> ImageList:
from torchvision.transforms.v2 import functional as func2

_init_default_device()
Expand All @@ -974,7 +972,7 @@ def flip_vertically(self) -> ImageList:
image_list._tensor = func2.vertical_flip(self._tensor)
return image_list

def flip_horizontally(self) -> ImageList:
def flip_left_and_right(self) -> ImageList:
from torchvision.transforms.v2 import functional as func2

_init_default_device()
Expand Down
26 changes: 13 additions & 13 deletions tests/safeds/data/image/containers/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import PIL.Image
import pytest
import torch
from syrupy import SnapshotAssertion
from torch.types import Device

from safeds._config import _get_device
from safeds.data.image.containers import Image
from safeds.data.image.typing import ImageSize
from safeds.data.tabular.containers import Table
from safeds.exceptions import IllegalFormatError, OutOfBoundsError
from syrupy import SnapshotAssertion
from torch.types import Device

from tests.helpers import (
configure_test_with_device,
device_cpu,
Expand Down Expand Up @@ -547,21 +547,21 @@ def test_should_warn_if_coordinates_outsize_image(


@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
class TestFlipVertically:
class TestFlipTopAndBottom:
@pytest.mark.parametrize(
"resource_path",
images_asymmetric(),
ids=images_asymmetric_ids(),
)
def test_should_flip_vertically(
def test_should_flip_top_and_bottom(
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_flip_v = image.flip_vertically()
image_flip_v = image.flip_top_and_bottom()
assert image != image_flip_v
assert image_flip_v == snapshot_png_image
_assert_width_height_channel(image, image_flip_v)
Expand All @@ -574,26 +574,26 @@ def test_should_flip_vertically(
def test_should_be_original(self, resource_path: str, device: Device) -> None:
configure_test_with_device(device)
image = Image.from_file(resolve_resource_path(resource_path))
image_flip_v_v = image.flip_vertically().flip_vertically()
image_flip_v_v = image.flip_top_and_bottom().flip_top_and_bottom()
assert image == image_flip_v_v


@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
class TestFlipHorizontally:
class TestFlipLeftAndRight:
@pytest.mark.parametrize(
"resource_path",
images_asymmetric(),
ids=images_asymmetric_ids(),
)
def test_should_flip_horizontally(
def test_should_flip_left_and_right(
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_flip_h = image.flip_horizontally()
image_flip_h = image.flip_left_and_right()
assert image != image_flip_h
assert image_flip_h == snapshot_png_image
_assert_width_height_channel(image, image_flip_h)
Expand All @@ -606,7 +606,7 @@ def test_should_flip_horizontally(
def test_should_be_original(self, resource_path: str, device: Device) -> None:
configure_test_with_device(device)
image = Image.from_file(resolve_resource_path(resource_path))
image_flip_h_h = image.flip_horizontally().flip_horizontally()
image_flip_h_h = image.flip_left_and_right().flip_left_and_right()
assert image == image_flip_h_h


Expand Down Expand Up @@ -1011,8 +1011,8 @@ def test_should_return_flipped_image(self, resource_path: str, device: Device) -
image = Image.from_file(resolve_resource_path(resource_path))
image_left_rotated = image.rotate_left().rotate_left()
image_right_rotated = image.rotate_right().rotate_right()
image_flipped_h_v = image.flip_horizontally().flip_vertically()
image_flipped_v_h = image.flip_horizontally().flip_vertically()
image_flipped_h_v = image.flip_left_and_right().flip_top_and_bottom()
image_flipped_v_h = image.flip_left_and_right().flip_top_and_bottom()
assert image_left_rotated == image_right_rotated
assert image_left_rotated == image_flipped_h_v
assert image_left_rotated == image_flipped_v_h
Expand Down
24 changes: 12 additions & 12 deletions tests/safeds/data/image/containers/test_image_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

import pytest
import torch
from syrupy import SnapshotAssertion
from torch import Tensor
from torch.types import Device

from safeds.data.image.containers import Image, ImageList
from safeds.data.image.containers._empty_image_list import _EmptyImageList
from safeds.data.image.containers._multi_size_image_list import _MultiSizeImageList
from safeds.data.image.containers._single_size_image_list import _SingleSizeImageList
from safeds.data.tabular.containers import Table
from safeds.exceptions import DuplicateIndexError, IllegalFormatError, IndexOutOfBoundsError, OutOfBoundsError
from syrupy import SnapshotAssertion
from torch import Tensor
from torch.types import Device

from tests.helpers import (
configure_test_with_device,
device_cpu,
Expand Down Expand Up @@ -892,8 +892,8 @@ class TestTransformsEqualImageTransforms:
("resize", [700, 400]),
("convert_to_grayscale", None),
("crop", [0, 0, 100, 100]),
("flip_vertically", None),
("flip_horizontally", None),
("flip_top_and_bottom", None),
("flip_left_and_right", None),
("adjust_brightness", [0]),
("adjust_brightness", [0.5]),
("adjust_brightness", [10]),
Expand All @@ -917,8 +917,8 @@ class TestTransformsEqualImageTransforms:
"resize-(700, 400)",
"grayscale",
"crop-(0, 0, 100, 100)",
"flip_vertically",
"flip_horizontally",
"flip_top_and_bottom",
"flip_left_and_right",
"adjust_brightness-zero factor",
"adjust_brightness-small factor",
"adjust_brightness-large factor",
Expand Down Expand Up @@ -1631,8 +1631,8 @@ def test_remove_image_by_index(self, device: Device) -> None:
("resize", [700, 400]),
("convert_to_grayscale", None),
("crop", [0, 0, 100, 100]),
("flip_vertically", None),
("flip_horizontally", None),
("flip_top_and_bottom", None),
("flip_left_and_right", None),
("adjust_brightness", [0]),
("adjust_brightness", [0.5]),
("adjust_brightness", [10]),
Expand Down Expand Up @@ -1663,8 +1663,8 @@ def test_remove_image_by_index(self, device: Device) -> None:
"resize-(700, 400)",
"grayscale",
"crop-(0, 0, 100, 100)",
"flip_vertically",
"flip_horizontally",
"flip_top_and_bottom",
"flip_left_and_right",
"adjust_brightness-zero factor",
"adjust_brightness-small factor",
"adjust_brightness-large factor",
Expand Down

0 comments on commit a3607d7

Please sign in to comment.