Skip to content

ImageList

An ImageList is a list of different images. It can hold different sizes of Images. The channel of all images is the same.

To create an ImageList call one of the following static methods:

Method Description
from_images Create an ImageList from a list of Images.
from_files Create an ImageList from a directory or a list of files.

Methods:

Name Description
add_image

Return a new ImageList with the given image added to the image list.

add_images

Return a new ImageList with the given images added to the image list.

add_noise

Return a new ImageList with noise added to all images.

adjust_brightness

Return a new ImageList where all images have the adjusted brightness.

adjust_color_balance

Return a new ImageList where all images have the adjusted color balance.

adjust_contrast

Return a new ImageList where all images have the adjusted contrast.

blur

Return a new ImageList where all images have been blurred.

change_channel

Return a new ImageList that has the given number of channels.

convert_to_grayscale

Return a new ImageList with all images converted to grayscale.

crop

Return a new ImageList with all images cropped to a given bounding rectangle.

find_edges

Return a new ImageList with grayscale versions of the images with the edges highlighted.

flip_left_and_right

Return a new ImageList where left and right sides of all images are flipped along a vertical axis.

flip_top_and_bottom

Return a new ImageList where top and bottom of all images are flipped along a horizontal axis.

from_files

Create an ImageList from a directory or a list of files.

from_images

Create an ImageList from a list of images.

get_image

Return the image at the given index.

has_image

Return whether the given image is in this image list.

index

Return a list of indexes of the given image.

invert_colors

Return a new ImageList where all images have their colors inverted.

remove_duplicate_images

Return a new ImageList with all duplicate images removed.

remove_image

Return a new ImageList with the given image removed from the image list.

remove_image_by_index

Return a new ImageList with the given indices removed from the image list.

remove_images

Return a new ImageList with the given images removed from the image list.

remove_images_with_size

Return a new ImageList with the all images of the given size removed.

resize

Return a new ImageList with all images resized to a given size.

rotate_left

Return a new ImageList where all images have been rotated 90 degrees counter-clockwise.

rotate_right

Return a new ImageList where all images have been rotated 90 degrees clockwise.

sharpen

Return a new ImageList where all images have been sharpened.

shuffle_images

Return a new ImageList with all images shuffled.

to_images

Return a list of all images in this image list.

to_jpeg_files

Save all images as jpeg files.

to_png_files

Save all images as png files.

Attributes:

Name Type Description
channel int

The channel of all images.

heights list[int]

A list of all heights in this image list.

image_count int

The number of images in this image list.

size_count int

The number of different sizes of images in this image list.

sizes list[ImageSize]

The sizes of all images.

widths list[int]

A list of all widths in this image list.

Source code in src/safeds/data/image/containers/_image_list.py
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
class ImageList(metaclass=ABCMeta):
    """
    An ImageList is a list of different images. It can hold different sizes of Images. The channel of all images is the same.

    To create an `ImageList` call one of the following static methods:

    | Method                                                                        | Description                                              |
    | ----------------------------------------------------------------------------- | -------------------------------------------------------- |
    | [from_images][safeds.data.image.containers._image_list.ImageList.from_images] | Create an ImageList from a list of Images.               |
    | [from_files][safeds.data.image.containers._image_list.ImageList.from_files]   | Create an ImageList from a directory or a list of files. |
    """

    @staticmethod
    @abstractmethod
    def _create_image_list(images: list[Tensor], indices: list[int]) -> ImageList:
        """
        Create an ImageList from a list of tensors.

        Parameters
        ----------
        images:
            the list of tensors
        indices:
            a list of indices for the tensors

        Returns
        -------
        image_list:
            The image list
        """

    @staticmethod
    def from_images(images: list[Image]) -> ImageList:
        """
        Create an ImageList from a list of images.

        Parameters
        ----------
        images:
            the list of images

        Returns
        -------
        image_list:
            the image list
        """
        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

        if len(images) == 0:
            return _EmptyImageList()

        indices = list(range(len(images)))
        first_width = images[0].width
        first_height = images[0].height
        for im in images:
            if first_width != im.width or first_height != im.height:
                return _MultiSizeImageList._create_image_list([image._image_tensor for image in images], indices)
        return _SingleSizeImageList._create_image_list([image._image_tensor for image in images], indices)

    @staticmethod
    @overload
    def from_files(path: str | Path | Sequence[str | Path]) -> ImageList: ...

    @staticmethod
    @overload
    def from_files(path: str | Path | Sequence[str | Path], *, load_percentage: float) -> ImageList: ...

    @staticmethod
    @overload
    def from_files(path: str | Path | Sequence[str | Path], *, return_filenames: Literal[False]) -> ImageList: ...

    @staticmethod
    @overload
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: Literal[False],
        load_percentage: float,
    ) -> ImageList: ...

    @staticmethod
    @overload
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: Literal[True],
    ) -> tuple[ImageList, list[str]]: ...

    @staticmethod
    @overload
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: Literal[True],
        load_percentage: float,
    ) -> tuple[ImageList, list[str]]: ...

    @staticmethod
    @overload
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: bool,
    ) -> ImageList | tuple[ImageList, list[str]]: ...

    @staticmethod
    @overload
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: bool,
        load_percentage: float,
    ) -> ImageList | tuple[ImageList, list[str]]: ...

    @staticmethod
    def from_files(
        path: str | Path | Sequence[str | Path],
        *,
        return_filenames: bool = False,
        load_percentage: float = 1.0,
    ) -> ImageList | tuple[ImageList, list[str]]:
        """
        Create an ImageList from a directory or a list of files.

        If you provide a path to a directory the images will be sorted alphabetically while inner directories will be sorted after image files.

        Parameters
        ----------
        path:
            the path to the directory or a list of files
        return_filenames:
            if True the output will be a tuple which contains a list of the filenames in order of the images
        load_percentage:
            the percentage of the given data being loaded. If below 1 the files will be shuffled before loading

        Returns
        -------
        image_list:
            the image list

        Raises
        ------
        FileNotFoundError
            If the directory or one of the files of the path cannot be found
        OutOfBoundsError
            If load_percentage is not between 0 and 1
        """
        from PIL.Image import open as pil_image_open

        _init_default_device()

        random.seed(_get_random_seed())

        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

        _check_bounds("load_percentage", load_percentage, lower_bound=_ClosedBound(0), upper_bound=_ClosedBound(1))

        if isinstance(path, list) and len(path) == 0:
            return _EmptyImageList()

        file_names = []

        path_list: list[str | Path]
        if isinstance(path, Path | str):
            path_list = [Path(path)]
        else:
            path_list = list(path)
        while len(path_list) != 0:
            p = Path(path_list.pop(0))
            if p.is_dir():
                path_list += sorted([p / name for name in os.listdir(p)])
            elif p.is_file():
                file_names.append(str(p))
            else:
                raise FileNotFoundError(f"No such file or directory: '{path}'")

        if load_percentage < 1:
            random.shuffle(file_names)
            file_names = file_names[: max(round(len(file_names) * load_percentage), 1) if load_percentage > 0 else 0]

        num_of_files = len(file_names)

        if num_of_files == 0:
            return _EmptyImageList()

        image_sizes: dict[tuple[int, int], dict[int, list[str]]] = {}
        image_indices: dict[tuple[int, int], dict[int, list[int]]] = {}
        image_count: dict[tuple[int, int], int] = {}
        max_channel = -1

        for i, filename in enumerate(file_names):
            im = pil_image_open(filename)
            im_channel = len(im.getbands())
            im_size = (im.width, im.height)
            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]}
                image_count[im_size] = 1
            elif im_channel not in image_sizes[im_size]:
                image_sizes[im_size][im_channel] = [filename]
                image_indices[im_size][im_channel] = [i]
                image_count[im_size] += 1
            else:
                image_sizes[im_size][im_channel].append(filename)
                image_indices[im_size][im_channel].append(i)
                image_count[im_size] += 1

        num_of_threads = min(math.ceil(num_of_files / 1000), 100)
        num_of_files_per_thread = math.ceil(num_of_files / num_of_threads)

        single_sized_image_lists = []
        thread_packages = []
        for size, image_files in image_sizes.items():
            im_list, packages = _SingleSizeImageList._create_image_list_from_files(
                image_files,
                image_count[size],
                max_channel,
                size[0],
                size[1],
                image_indices[size],
                num_of_files_per_thread,
            )
            single_sized_image_lists.append(im_list._as_single_size_image_list())
            thread_packages += packages
        thread_packages.sort(key=lambda x: len(x), reverse=True)

        threads: list[ImageList._FromImageThread] = []
        for thread_index in range(num_of_threads):
            current_thread_workload = 0
            current_thread_packages = []
            while current_thread_workload < num_of_files_per_thread and len(thread_packages) > 0:
                next_package = thread_packages.pop()
                current_thread_packages.append(next_package)
                current_thread_workload += len(next_package)
            if thread_index == num_of_threads - 1 and len(thread_packages) > 0:
                current_thread_packages += thread_packages  # pragma: no cover
            thread = ImageList._FromImageThread(current_thread_packages)
            threads.append(thread)
            thread.start()
        for thread in threads:
            thread.join()

        if len(single_sized_image_lists) == 1:
            image_list: ImageList = single_sized_image_lists[0]
        else:
            image_list = _MultiSizeImageList._create_from_single_sized_image_lists(single_sized_image_lists)

        if return_filenames:
            return image_list, file_names
        else:
            return image_list

    class _FromFileThreadPackage:
        def __init__(
            self,
            im_files: list[str],
            im_channel: int,
            to_channel: int,
            im_width: int,
            im_height: int,
            tensor: Tensor,
            start_index: int,
        ) -> None:
            self._im_files = im_files
            self._im_channel = im_channel
            self._to_channel = to_channel
            self._im_width = im_width
            self._im_height = im_height
            self._tensor = tensor
            self._start_index = start_index

        def load_files(self) -> None:
            import torch
            from torchvision.io import read_image

            _init_default_device()

            num_of_files = len(self._im_files)
            tensor_channel = max(self._im_channel, min(self._to_channel, 3))
            for index, im in enumerate(self._im_files):
                self._tensor[index + self._start_index, 0:tensor_channel] = read_image(im)
            if self._to_channel == 4 and self._im_channel < 4:
                torch.full(
                    (num_of_files, 1, self._im_height, self._im_width),
                    255,
                    out=self._tensor[self._start_index : self._start_index + num_of_files, 3:4],
                )

        def __len__(self) -> int:
            return len(self._im_files)

    class _FromImageThread(Thread):
        def __init__(self, packages: list[ImageList._FromFileThreadPackage]) -> None:
            super().__init__()
            self._packages = packages

        def run(self) -> None:
            for pck in self._packages:
                pck.load_files()

    @abstractmethod
    def _clone(self) -> ImageList:
        """
        Clone your ImageList to a new instance.

        Returns
        -------
        image_list:
            the cloned image list
        """

    @abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Compare two image lists.

        Two image lists are only equal if they contain the same images at the same indices.

        Parameters
        ----------
        other:
            the image list to compare to

        Returns
        -------
        equals:
            Whether the two image lists are equal
        """

    @abstractmethod
    def __hash__(self) -> int:
        """
        Return a deterministic hash value for this image list.

        Returns
        -------
        hash:
            The hash value.
        """

    @abstractmethod
    def __sizeof__(self) -> int:
        """
        Return the complete size of this object.

        Returns
        -------
        size:
            Size of this object in bytes.
        """

    def __len__(self) -> int:
        """
        Return the number of images in this image list.

        Returns
        -------
        length:
            The number of images
        """
        return self.image_count

    def __contains__(self, item: object) -> bool:
        """
        Return whether the given item is in this image list.

        Parameters
        ----------
        item:
            the item to check

        Returns
        -------
        has_item:
            Whether the given item is in this image list
        """
        return isinstance(item, Image) and self.has_image(item)

    def _repr_png_(self) -> bytes:
        """
        Return a PNG representation of this image list as bytes.

        Returns
        -------
        png:
            the png representation of this image list
        """
        import torch
        from torchvision.utils import make_grid, save_image

        _init_default_device()

        from safeds.data.image.containers._empty_image_list import _EmptyImageList

        if isinstance(self, _EmptyImageList):
            raise TypeError("You cannot display an empty ImageList")

        max_width, max_height = max(self.widths), max(self.heights)
        tensors = []
        for image in self.to_images():
            im_tensor = torch.zeros([4, max_height, max_width])
            im_tensor[:, : image.height, : image.width] = image.change_channel(4)._image_tensor
            tensors.append(im_tensor)
        tensor_grid = make_grid(tensors, math.ceil(math.sqrt(len(tensors))))
        buffer = io.BytesIO()
        save_image(tensor_grid.to(torch.float32) / 255, buffer, format="png")
        buffer.seek(0)
        return buffer.read()

    # ------------------------------------------------------------------------------------------------------------------
    # Properties
    # ------------------------------------------------------------------------------------------------------------------

    @property
    @abstractmethod
    def image_count(self) -> int:
        """The number of images in this image list."""

    @property
    @abstractmethod
    def widths(self) -> list[int]:
        """A list of all widths in this image list."""

    @property
    @abstractmethod
    def heights(self) -> list[int]:
        """A list of all heights in this image list."""

    @property
    @abstractmethod
    def channel(self) -> int:
        """The channel of all images."""

    @property
    @abstractmethod
    def sizes(self) -> list[ImageSize]:
        """The sizes of all images."""

    @property
    @abstractmethod
    def size_count(self) -> int:
        """The number of different sizes of images in this image list."""

    # ------------------------------------------------------------------------------------------------------------------
    # Getters
    # ------------------------------------------------------------------------------------------------------------------

    @abstractmethod
    def get_image(self, index: int) -> Image:
        """
        Return the image at the given index.

        Parameters
        ----------
        index:
            the index for the image to return

        Returns
        -------
        image:
            the image at the given index
        """

    @abstractmethod
    def index(self, image: Image) -> list[int]:
        """
        Return a list of indexes of the given image.

        If the image has multiple occurrences, all indices will be returned

        Parameters
        ----------
        image:
            the image to search for occurrences

        Returns
        -------
        indices:
            all occurrences of the image
        """

    @abstractmethod
    def has_image(self, image: Image) -> bool:
        """
        Return whether the given image is in this image list.

        Parameters
        ----------
        image:
            the image to check

        Returns
        -------
        has_image:
            Whether the given image is in this image list
        """

    # ------------------------------------------------------------------------------------------------------------------
    # Conversion
    # ------------------------------------------------------------------------------------------------------------------

    @abstractmethod
    def to_jpeg_files(self, path: str | Path | list[str | Path]) -> None:
        """
        Save all images as jpeg files.

        Parameters
        ----------
        path:
            Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

        Raises
        ------
        IllegalFormatError
            If the channel of the images is not supported
        ValueError
            If the path is a list but has too few or too many entries
        """

    @abstractmethod
    def to_png_files(self, path: str | Path | list[str | Path]) -> None:
        """
        Save all images as png files.

        Parameters
        ----------
        path:
            Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

        Raises
        ------
        ValueError
            If the path is a list but has too few or too many entries
        """

    @abstractmethod
    def to_images(self, indices: list[int] | None = None) -> list[Image]:
        """
        Return a list of all images in this image list.

        Parameters
        ----------
        indices:
            a list of all indices to include in the output. If None, all indices will be included

        Returns
        -------
        images:
            the list of all images

        Raises
        ------
        IndexOutOfBoundsError
            If any index is out of bounds
        """

    def _as_multi_size_image_list(self) -> _MultiSizeImageList:
        """
        Typechecking method for MultiSizeImageList.

        Returns
        -------
        self:
            self as a MultiSizeImageList

        Raises
        ------
        ValueError
            if this image list is not a MultiSizeImageList
        """
        from safeds.data.image.containers._multi_size_image_list import _MultiSizeImageList

        if isinstance(self, _MultiSizeImageList):
            return self
        raise ValueError("The given image_list is not a MultiSizeImageList")

    def _as_single_size_image_list(self) -> _SingleSizeImageList:
        """
        Typechecking method for SingleSizeImageList.

        Returns
        -------
        self:
            self as a SingleSizeImageList

        Raises
        ------
        ValueError
            if this image list is not a SingleSizeImageList
        """
        from safeds.data.image.containers._single_size_image_list import _SingleSizeImageList

        if isinstance(self, _SingleSizeImageList):
            return self
        raise ValueError("The given image_list is not a SingleSizeImageList")

    # ------------------------------------------------------------------------------------------------------------------
    # Transformations
    # ------------------------------------------------------------------------------------------------------------------

    @abstractmethod
    def change_channel(self, channel: int) -> ImageList:
        """
        Return a new `ImageList` that has the given number of channels.

        The original image list is not modified.

        Parameters
        ----------
        channel:
            The new number of channels. 1 will result in grayscale images

        Returns
        -------
        image_list:
            the image list with the given number of channels

        Raises
        ------
        ValueError:
            if the given channel is not a valid channel option
        """

    @abstractmethod
    def _add_image_tensor(self, image_tensor: Tensor, index: int) -> ImageList:
        """
        Return a new `ImageList` with the given tensor added as an image.

        The original image list is not modified.

        Parameters
        ----------
        image_tensor:
            The new tensor to be added to the image list

        Returns
        -------
        image_list:
            the image list with the new tensor added
        """

    def add_image(self, image: Image) -> ImageList:
        """
        Return a new `ImageList` with the given image added to the image list.

        The original image list is not modified.

        Parameters
        ----------
        image:
            The image to be added to the image list

        Returns
        -------
        image_list:
            the image list with the new image added
        """
        return self._add_image_tensor(image._image_tensor, self.image_count)

    @abstractmethod
    def add_images(self, images: list[Image] | ImageList) -> ImageList:
        """
        Return a new `ImageList` with the given images added to the image list.

        The original image list is not modified.

        Parameters
        ----------
        images:
            The images to be added to the image list

        Returns
        -------
        image_list:
            the image list with the new images added
        """

    def remove_image(self, image: Image) -> ImageList:
        """
        Return a new `ImageList` with the given image removed from the image list.

        If the image has multiple occurrences, all occurrences will be removed.

        The original image list is not modified.

        Parameters
        ----------
        image:
            The image to be removed from the image list

        Returns
        -------
        image_list:
            the image list with the given image removed
        """
        return self._remove_image_by_index_ignore_invalid(self.index(image))

    def remove_images(self, images: list[Image]) -> ImageList:
        """
        Return a new `ImageList` with the given images removed from the image list.

        If one image has multiple occurrences, all occurrences will be removed.

        The original image list is not modified.

        Parameters
        ----------
        images:
            The images to be removed from the image list

        Returns
        -------
        image_list:
            the image list with the given images removed
        """
        indices_to_remove = []
        for image in images:
            indices_to_remove += self.index(image)
        return self._remove_image_by_index_ignore_invalid(list(set(indices_to_remove)))

    @abstractmethod
    def remove_image_by_index(self, index: int | list[int]) -> ImageList:
        """
        Return a new `ImageList` with the given indices removed from the image list.

        The original image list is not modified.

        Parameters
        ----------
        index:
            The index of the image to be removed from the image list

        Returns
        -------
        image_list:
            the image list with the without the removed image

        Raises
        ------
        IndexOutOfBoundsError
            If one of the indices is out of bounds
        """

    @abstractmethod
    def _remove_image_by_index_ignore_invalid(self, index: int | list[int]) -> ImageList:
        """
        Return a new `ImageList` with the given indices removed from the image list.

        Invalid indices will be ignored.

        The original image list is not modified.

        Parameters
        ----------
        index:
            The index of the image to be removed from the image list

        Returns
        -------
        image_list:
            the image list with the without the removed image
        """

    @abstractmethod
    def remove_images_with_size(self, width: int, height: int) -> ImageList:
        """
        Return a new `ImageList` with the all images of the given size removed.

        The original image list is not modified.

        Parameters
        ----------
        width:
            The width of the images to be removed from the image list
        height:
            The height of the images to be removed from the image list

        Returns
        -------
        image_list:
            the image list with the given images removed

        Raises
        ------
        OutOfBoundsError
            If width or height are below 1
        """

    @abstractmethod
    def remove_duplicate_images(self) -> ImageList:
        """
        Return a new `ImageList` with all duplicate images removed.

        One occurrence of each image will stay in the image list.

        The original image list is not modified.

        Returns
        -------
        image_list:
            the image list with only unique images
        """

    @abstractmethod
    def shuffle_images(self) -> ImageList:
        """
        Return a new `ImageList` with all images shuffled.

        The original image list is not modified.

        Returns
        -------
        image_list:
            the image list with shuffled images
        """

    @abstractmethod
    def resize(self, new_width: int, new_height: int) -> ImageList:
        """
        Return a new `ImageList` with all images resized to a given size.

        The original image list is not modified.

        Parameters
        ----------
        new_width:
            the new width of the images
        new_height:
            the new height of the images

        Returns
        -------
        image_list:
            The image list with all images resized to the given width and height.

        Raises
        ------
        OutOfBoundsError
            If new_width or new_height are below 1
        """

    @abstractmethod
    def convert_to_grayscale(self) -> ImageList:
        """
        Return a new `ImageList` with all images converted to grayscale.

        The new image list will have the same amount of channels as the original image list.
        If you want to change the amount of channels used, please use the method [change_channel][safeds.data.image.containers._image_list.ImageList.change_channel].

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with all images converted to grayscale.
        """

    @abstractmethod
    def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
        """
        Return a new `ImageList` with all images cropped to a given bounding rectangle.

        The original image list is not modified.

        Parameters
        ----------
        x:
            the x coordinate of the top-left corner of the bounding rectangle
        y:
            the y coordinate of the top-left corner of the bounding rectangle
        width:
            the width of the bounding rectangle
        height:
            the height of the bounding rectangle

        Returns
        -------
        image_list:
            The image list with all images cropped

        Raises
        ------
        OutOfBoundsError
            If x or y are below 0 or if width or height are below 1
        """

    @abstractmethod
    def flip_top_and_bottom(self) -> ImageList:
        """
        Return a new `ImageList` where top and bottom of all images are flipped along a horizontal axis.

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with all images flipped vertically
        """

    @abstractmethod
    def flip_left_and_right(self) -> ImageList:
        """
        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.

        Returns
        -------
        image_list:
            The image list with all images flipped horizontally
        """

    @abstractmethod
    def adjust_brightness(self, factor: float) -> ImageList:
        """
        Return a new `ImageList` where all images have the adjusted brightness.

        The original image list is not modified.

        Parameters
        ----------
        factor:
            The brightness factor.
            1.0 will not change the brightness.
            Below 1.0 will result in a darker images.
            Above 1.0 will resolut in a brighter images.
            Has to be bigger than or equal to 0 (black).

        Returns
        -------
        image_list:
            The image list with adjusted brightness

        Raises
        ------
        OutOfBoundsError
            If factor is smaller than 0.
        """

    @abstractmethod
    def add_noise(self, standard_deviation: float) -> ImageList:
        """
        Return a new `ImageList` with noise added to all images.

        The original image list is not modified.

        Parameters
        ----------
        standard_deviation:
            The standard deviation of the normal distribution. Has to be bigger than or equal to 0.

        Returns
        -------
        image_list:
            The image list with added noise

        Raises
        ------
        OutOfBoundsError
            If standard_deviation is smaller than 0.
        """

    @abstractmethod
    def adjust_contrast(self, factor: float) -> ImageList:
        """
        Return a new `ImageList` where all images have the adjusted contrast.

        The original image list is not modified.

        Parameters
        ----------
        factor:
            If factor > 1, increase contrast of images.
            If factor = 1, no changes will be made.
            If factor < 1, make images greyer.
            Has to be bigger than or equal to 0 (gray).

        Returns
        -------
        image_list:
            The image list with adjusted contrast

        Raises
        ------
        OutOfBoundsError
            If factor is smaller than 0.
        """

    @abstractmethod
    def adjust_color_balance(self, factor: float) -> ImageList:
        """
        Return a new `ImageList` where all images have the adjusted color balance.

        The original image list is not modified.

        Parameters
        ----------
        factor:
            Has to be bigger than or equal to 0.
            If 0 <= factor < 1, make images greyer.
            If factor = 1, no changes will be made.
            If factor > 1, increase color balance of images.

        Returns
        -------
        image_list:
            The image list with adjusted color balance

        Raises
        ------
        OutOfBoundsError
            If factor is smaller than 0.
        """

    @abstractmethod
    def blur(self, radius: int) -> ImageList:
        """
        Return a new `ImageList` where all images have been blurred.

        The original image list is not modified.

        Parameters
        ----------
        radius:
             Radius is directly proportional to the blur value. The radius is equal to the amount of pixels united in
             each direction. A radius of 1 will result in a united box of 9 pixels.

        Returns
        -------
        image_list:
            The image list with blurred images

        Raises
        ------
        OutOfBoundsError
            If radius is smaller than 0 or equal or greater than the smallest size of one of the images.
        """

    @abstractmethod
    def sharpen(self, factor: float) -> ImageList:
        """
        Return a new `ImageList` where all images have been sharpened.

        The original image list is not modified.

        Parameters
        ----------
        factor:
            If factor > 1, increase the sharpness of the images.
            If factor = 1, no changes will be made.
            If factor < 1, blur the images.
            Has to be bigger than or equal to 0 (blurred).

        Returns
        -------
        image_list:
            The image list with sharpened images

        Raises
        ------
        OutOfBoundsError
            If factor is smaller than 0.
        """

    @abstractmethod
    def invert_colors(self) -> ImageList:
        """
        Return a new `ImageList` where all images have their colors inverted.

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with inverted colors
        """

    @abstractmethod
    def rotate_right(self) -> ImageList:
        """
        Return a new `ImageList` where all images have been rotated 90 degrees clockwise.

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with all images rotated
        """

    @abstractmethod
    def rotate_left(self) -> ImageList:
        """
        Return a new `ImageList` where all images have been rotated 90 degrees counter-clockwise.

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with all images rotated
        """

    @abstractmethod
    def find_edges(self) -> ImageList:
        """
        Return a new `ImageList` with grayscale versions of the images with the edges highlighted.

        The original image list is not modified.

        Returns
        -------
        image_list:
            The image list with highlighted edges
        """

channel

The channel of all images.

heights

A list of all heights in this image list.

image_count

The number of images in this image list.

size_count

The number of different sizes of images in this image list.

sizes

The sizes of all images.

widths

A list of all widths in this image list.

add_image

Return a new ImageList with the given image added to the image list.

The original image list is not modified.

Parameters:

Name Type Description Default
image Image

The image to be added to the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the new image added

Source code in src/safeds/data/image/containers/_image_list.py
def add_image(self, image: Image) -> ImageList:
    """
    Return a new `ImageList` with the given image added to the image list.

    The original image list is not modified.

    Parameters
    ----------
    image:
        The image to be added to the image list

    Returns
    -------
    image_list:
        the image list with the new image added
    """
    return self._add_image_tensor(image._image_tensor, self.image_count)

add_images

Return a new ImageList with the given images added to the image list.

The original image list is not modified.

Parameters:

Name Type Description Default
images list[Image] | ImageList

The images to be added to the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the new images added

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def add_images(self, images: list[Image] | ImageList) -> ImageList:
    """
    Return a new `ImageList` with the given images added to the image list.

    The original image list is not modified.

    Parameters
    ----------
    images:
        The images to be added to the image list

    Returns
    -------
    image_list:
        the image list with the new images added
    """

add_noise

Return a new ImageList with noise added to all images.

The original image list is not modified.

Parameters:

Name Type Description Default
standard_deviation float

The standard deviation of the normal distribution. Has to be bigger than or equal to 0.

required

Returns:

Name Type Description
image_list ImageList

The image list with added noise

Raises:

Type Description
OutOfBoundsError

If standard_deviation is smaller than 0.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def add_noise(self, standard_deviation: float) -> ImageList:
    """
    Return a new `ImageList` with noise added to all images.

    The original image list is not modified.

    Parameters
    ----------
    standard_deviation:
        The standard deviation of the normal distribution. Has to be bigger than or equal to 0.

    Returns
    -------
    image_list:
        The image list with added noise

    Raises
    ------
    OutOfBoundsError
        If standard_deviation is smaller than 0.
    """

adjust_brightness

Return a new ImageList where all images have the adjusted brightness.

The original image list is not modified.

Parameters:

Name Type Description Default
factor float

The brightness factor. 1.0 will not change the brightness. Below 1.0 will result in a darker images. Above 1.0 will resolut in a brighter images. Has to be bigger than or equal to 0 (black).

required

Returns:

Name Type Description
image_list ImageList

The image list with adjusted brightness

Raises:

Type Description
OutOfBoundsError

If factor is smaller than 0.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def adjust_brightness(self, factor: float) -> ImageList:
    """
    Return a new `ImageList` where all images have the adjusted brightness.

    The original image list is not modified.

    Parameters
    ----------
    factor:
        The brightness factor.
        1.0 will not change the brightness.
        Below 1.0 will result in a darker images.
        Above 1.0 will resolut in a brighter images.
        Has to be bigger than or equal to 0 (black).

    Returns
    -------
    image_list:
        The image list with adjusted brightness

    Raises
    ------
    OutOfBoundsError
        If factor is smaller than 0.
    """

adjust_color_balance

Return a new ImageList where all images have the adjusted color balance.

The original image list is not modified.

Parameters:

Name Type Description Default
factor float

Has to be bigger than or equal to 0. If 0 <= factor < 1, make images greyer. If factor = 1, no changes will be made. If factor > 1, increase color balance of images.

required

Returns:

Name Type Description
image_list ImageList

The image list with adjusted color balance

Raises:

Type Description
OutOfBoundsError

If factor is smaller than 0.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def adjust_color_balance(self, factor: float) -> ImageList:
    """
    Return a new `ImageList` where all images have the adjusted color balance.

    The original image list is not modified.

    Parameters
    ----------
    factor:
        Has to be bigger than or equal to 0.
        If 0 <= factor < 1, make images greyer.
        If factor = 1, no changes will be made.
        If factor > 1, increase color balance of images.

    Returns
    -------
    image_list:
        The image list with adjusted color balance

    Raises
    ------
    OutOfBoundsError
        If factor is smaller than 0.
    """

adjust_contrast

Return a new ImageList where all images have the adjusted contrast.

The original image list is not modified.

Parameters:

Name Type Description Default
factor float

If factor > 1, increase contrast of images. If factor = 1, no changes will be made. If factor < 1, make images greyer. Has to be bigger than or equal to 0 (gray).

required

Returns:

Name Type Description
image_list ImageList

The image list with adjusted contrast

Raises:

Type Description
OutOfBoundsError

If factor is smaller than 0.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def adjust_contrast(self, factor: float) -> ImageList:
    """
    Return a new `ImageList` where all images have the adjusted contrast.

    The original image list is not modified.

    Parameters
    ----------
    factor:
        If factor > 1, increase contrast of images.
        If factor = 1, no changes will be made.
        If factor < 1, make images greyer.
        Has to be bigger than or equal to 0 (gray).

    Returns
    -------
    image_list:
        The image list with adjusted contrast

    Raises
    ------
    OutOfBoundsError
        If factor is smaller than 0.
    """

blur

Return a new ImageList where all images have been blurred.

The original image list is not modified.

Parameters:

Name Type Description Default
radius int

Radius is directly proportional to the blur value. The radius is equal to the amount of pixels united in each direction. A radius of 1 will result in a united box of 9 pixels.

required

Returns:

Name Type Description
image_list ImageList

The image list with blurred images

Raises:

Type Description
OutOfBoundsError

If radius is smaller than 0 or equal or greater than the smallest size of one of the images.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def blur(self, radius: int) -> ImageList:
    """
    Return a new `ImageList` where all images have been blurred.

    The original image list is not modified.

    Parameters
    ----------
    radius:
         Radius is directly proportional to the blur value. The radius is equal to the amount of pixels united in
         each direction. A radius of 1 will result in a united box of 9 pixels.

    Returns
    -------
    image_list:
        The image list with blurred images

    Raises
    ------
    OutOfBoundsError
        If radius is smaller than 0 or equal or greater than the smallest size of one of the images.
    """

change_channel

Return a new ImageList that has the given number of channels.

The original image list is not modified.

Parameters:

Name Type Description Default
channel int

The new number of channels. 1 will result in grayscale images

required

Returns:

Name Type Description
image_list ImageList

the image list with the given number of channels

Raises:

Type Description
ValueError:

if the given channel is not a valid channel option

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def change_channel(self, channel: int) -> ImageList:
    """
    Return a new `ImageList` that has the given number of channels.

    The original image list is not modified.

    Parameters
    ----------
    channel:
        The new number of channels. 1 will result in grayscale images

    Returns
    -------
    image_list:
        the image list with the given number of channels

    Raises
    ------
    ValueError:
        if the given channel is not a valid channel option
    """

convert_to_grayscale

Return a new ImageList with all images converted to grayscale.

The new image list will have the same amount of channels as the original image list. If you want to change the amount of channels used, please use the method change_channel.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with all images converted to grayscale.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def convert_to_grayscale(self) -> ImageList:
    """
    Return a new `ImageList` with all images converted to grayscale.

    The new image list will have the same amount of channels as the original image list.
    If you want to change the amount of channels used, please use the method [change_channel][safeds.data.image.containers._image_list.ImageList.change_channel].

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with all images converted to grayscale.
    """

crop

Return a new ImageList with all images cropped to a given bounding rectangle.

The original image list is not modified.

Parameters:

Name Type Description Default
x int

the x coordinate of the top-left corner of the bounding rectangle

required
y int

the y coordinate of the top-left corner of the bounding rectangle

required
width int

the width of the bounding rectangle

required
height int

the height of the bounding rectangle

required

Returns:

Name Type Description
image_list ImageList

The image list with all images cropped

Raises:

Type Description
OutOfBoundsError

If x or y are below 0 or if width or height are below 1

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def crop(self, x: int, y: int, width: int, height: int) -> ImageList:
    """
    Return a new `ImageList` with all images cropped to a given bounding rectangle.

    The original image list is not modified.

    Parameters
    ----------
    x:
        the x coordinate of the top-left corner of the bounding rectangle
    y:
        the y coordinate of the top-left corner of the bounding rectangle
    width:
        the width of the bounding rectangle
    height:
        the height of the bounding rectangle

    Returns
    -------
    image_list:
        The image list with all images cropped

    Raises
    ------
    OutOfBoundsError
        If x or y are below 0 or if width or height are below 1
    """

find_edges

Return a new ImageList with grayscale versions of the images with the edges highlighted.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with highlighted edges

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def find_edges(self) -> ImageList:
    """
    Return a new `ImageList` with grayscale versions of the images with the edges highlighted.

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with highlighted edges
    """

flip_left_and_right

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.

Returns:

Name Type Description
image_list ImageList

The image list with all images flipped horizontally

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def flip_left_and_right(self) -> ImageList:
    """
    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.

    Returns
    -------
    image_list:
        The image list with all images flipped horizontally
    """

flip_top_and_bottom

Return a new ImageList where top and bottom of all images are flipped along a horizontal axis.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with all images flipped vertically

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def flip_top_and_bottom(self) -> ImageList:
    """
    Return a new `ImageList` where top and bottom of all images are flipped along a horizontal axis.

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with all images flipped vertically
    """

from_files

from_files
from_files
from_files
from_files
from_files
from_files
from_files
from_files

Create an ImageList from a directory or a list of files.

If you provide a path to a directory the images will be sorted alphabetically while inner directories will be sorted after image files.

Parameters:

Name Type Description Default
path str | Path | Sequence[str | Path]

the path to the directory or a list of files

required
return_filenames bool

if True the output will be a tuple which contains a list of the filenames in order of the images

False
load_percentage float

the percentage of the given data being loaded. If below 1 the files will be shuffled before loading

1.0

Returns:

Name Type Description
image_list ImageList | tuple[ImageList, list[str]]

the image list

Raises:

Type Description
FileNotFoundError

If the directory or one of the files of the path cannot be found

OutOfBoundsError

If load_percentage is not between 0 and 1

Source code in src/safeds/data/image/containers/_image_list.py
@staticmethod
def from_files(
    path: str | Path | Sequence[str | Path],
    *,
    return_filenames: bool = False,
    load_percentage: float = 1.0,
) -> ImageList | tuple[ImageList, list[str]]:
    """
    Create an ImageList from a directory or a list of files.

    If you provide a path to a directory the images will be sorted alphabetically while inner directories will be sorted after image files.

    Parameters
    ----------
    path:
        the path to the directory or a list of files
    return_filenames:
        if True the output will be a tuple which contains a list of the filenames in order of the images
    load_percentage:
        the percentage of the given data being loaded. If below 1 the files will be shuffled before loading

    Returns
    -------
    image_list:
        the image list

    Raises
    ------
    FileNotFoundError
        If the directory or one of the files of the path cannot be found
    OutOfBoundsError
        If load_percentage is not between 0 and 1
    """
    from PIL.Image import open as pil_image_open

    _init_default_device()

    random.seed(_get_random_seed())

    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

    _check_bounds("load_percentage", load_percentage, lower_bound=_ClosedBound(0), upper_bound=_ClosedBound(1))

    if isinstance(path, list) and len(path) == 0:
        return _EmptyImageList()

    file_names = []

    path_list: list[str | Path]
    if isinstance(path, Path | str):
        path_list = [Path(path)]
    else:
        path_list = list(path)
    while len(path_list) != 0:
        p = Path(path_list.pop(0))
        if p.is_dir():
            path_list += sorted([p / name for name in os.listdir(p)])
        elif p.is_file():
            file_names.append(str(p))
        else:
            raise FileNotFoundError(f"No such file or directory: '{path}'")

    if load_percentage < 1:
        random.shuffle(file_names)
        file_names = file_names[: max(round(len(file_names) * load_percentage), 1) if load_percentage > 0 else 0]

    num_of_files = len(file_names)

    if num_of_files == 0:
        return _EmptyImageList()

    image_sizes: dict[tuple[int, int], dict[int, list[str]]] = {}
    image_indices: dict[tuple[int, int], dict[int, list[int]]] = {}
    image_count: dict[tuple[int, int], int] = {}
    max_channel = -1

    for i, filename in enumerate(file_names):
        im = pil_image_open(filename)
        im_channel = len(im.getbands())
        im_size = (im.width, im.height)
        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]}
            image_count[im_size] = 1
        elif im_channel not in image_sizes[im_size]:
            image_sizes[im_size][im_channel] = [filename]
            image_indices[im_size][im_channel] = [i]
            image_count[im_size] += 1
        else:
            image_sizes[im_size][im_channel].append(filename)
            image_indices[im_size][im_channel].append(i)
            image_count[im_size] += 1

    num_of_threads = min(math.ceil(num_of_files / 1000), 100)
    num_of_files_per_thread = math.ceil(num_of_files / num_of_threads)

    single_sized_image_lists = []
    thread_packages = []
    for size, image_files in image_sizes.items():
        im_list, packages = _SingleSizeImageList._create_image_list_from_files(
            image_files,
            image_count[size],
            max_channel,
            size[0],
            size[1],
            image_indices[size],
            num_of_files_per_thread,
        )
        single_sized_image_lists.append(im_list._as_single_size_image_list())
        thread_packages += packages
    thread_packages.sort(key=lambda x: len(x), reverse=True)

    threads: list[ImageList._FromImageThread] = []
    for thread_index in range(num_of_threads):
        current_thread_workload = 0
        current_thread_packages = []
        while current_thread_workload < num_of_files_per_thread and len(thread_packages) > 0:
            next_package = thread_packages.pop()
            current_thread_packages.append(next_package)
            current_thread_workload += len(next_package)
        if thread_index == num_of_threads - 1 and len(thread_packages) > 0:
            current_thread_packages += thread_packages  # pragma: no cover
        thread = ImageList._FromImageThread(current_thread_packages)
        threads.append(thread)
        thread.start()
    for thread in threads:
        thread.join()

    if len(single_sized_image_lists) == 1:
        image_list: ImageList = single_sized_image_lists[0]
    else:
        image_list = _MultiSizeImageList._create_from_single_sized_image_lists(single_sized_image_lists)

    if return_filenames:
        return image_list, file_names
    else:
        return image_list

from_images

Create an ImageList from a list of images.

Parameters:

Name Type Description Default
images list[Image]

the list of images

required

Returns:

Name Type Description
image_list ImageList

the image list

Source code in src/safeds/data/image/containers/_image_list.py
@staticmethod
def from_images(images: list[Image]) -> ImageList:
    """
    Create an ImageList from a list of images.

    Parameters
    ----------
    images:
        the list of images

    Returns
    -------
    image_list:
        the image list
    """
    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

    if len(images) == 0:
        return _EmptyImageList()

    indices = list(range(len(images)))
    first_width = images[0].width
    first_height = images[0].height
    for im in images:
        if first_width != im.width or first_height != im.height:
            return _MultiSizeImageList._create_image_list([image._image_tensor for image in images], indices)
    return _SingleSizeImageList._create_image_list([image._image_tensor for image in images], indices)

get_image

Return the image at the given index.

Parameters:

Name Type Description Default
index int

the index for the image to return

required

Returns:

Name Type Description
image Image

the image at the given index

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def get_image(self, index: int) -> Image:
    """
    Return the image at the given index.

    Parameters
    ----------
    index:
        the index for the image to return

    Returns
    -------
    image:
        the image at the given index
    """

has_image

Return whether the given image is in this image list.

Parameters:

Name Type Description Default
image Image

the image to check

required

Returns:

Name Type Description
has_image bool

Whether the given image is in this image list

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def has_image(self, image: Image) -> bool:
    """
    Return whether the given image is in this image list.

    Parameters
    ----------
    image:
        the image to check

    Returns
    -------
    has_image:
        Whether the given image is in this image list
    """

index

Return a list of indexes of the given image.

If the image has multiple occurrences, all indices will be returned

Parameters:

Name Type Description Default
image Image

the image to search for occurrences

required

Returns:

Name Type Description
indices list[int]

all occurrences of the image

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def index(self, image: Image) -> list[int]:
    """
    Return a list of indexes of the given image.

    If the image has multiple occurrences, all indices will be returned

    Parameters
    ----------
    image:
        the image to search for occurrences

    Returns
    -------
    indices:
        all occurrences of the image
    """

invert_colors

Return a new ImageList where all images have their colors inverted.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with inverted colors

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def invert_colors(self) -> ImageList:
    """
    Return a new `ImageList` where all images have their colors inverted.

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with inverted colors
    """

remove_duplicate_images

Return a new ImageList with all duplicate images removed.

One occurrence of each image will stay in the image list.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

the image list with only unique images

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def remove_duplicate_images(self) -> ImageList:
    """
    Return a new `ImageList` with all duplicate images removed.

    One occurrence of each image will stay in the image list.

    The original image list is not modified.

    Returns
    -------
    image_list:
        the image list with only unique images
    """

remove_image

Return a new ImageList with the given image removed from the image list.

If the image has multiple occurrences, all occurrences will be removed.

The original image list is not modified.

Parameters:

Name Type Description Default
image Image

The image to be removed from the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the given image removed

Source code in src/safeds/data/image/containers/_image_list.py
def remove_image(self, image: Image) -> ImageList:
    """
    Return a new `ImageList` with the given image removed from the image list.

    If the image has multiple occurrences, all occurrences will be removed.

    The original image list is not modified.

    Parameters
    ----------
    image:
        The image to be removed from the image list

    Returns
    -------
    image_list:
        the image list with the given image removed
    """
    return self._remove_image_by_index_ignore_invalid(self.index(image))

remove_image_by_index

Return a new ImageList with the given indices removed from the image list.

The original image list is not modified.

Parameters:

Name Type Description Default
index int | list[int]

The index of the image to be removed from the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the without the removed image

Raises:

Type Description
IndexOutOfBoundsError

If one of the indices is out of bounds

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def remove_image_by_index(self, index: int | list[int]) -> ImageList:
    """
    Return a new `ImageList` with the given indices removed from the image list.

    The original image list is not modified.

    Parameters
    ----------
    index:
        The index of the image to be removed from the image list

    Returns
    -------
    image_list:
        the image list with the without the removed image

    Raises
    ------
    IndexOutOfBoundsError
        If one of the indices is out of bounds
    """

remove_images

Return a new ImageList with the given images removed from the image list.

If one image has multiple occurrences, all occurrences will be removed.

The original image list is not modified.

Parameters:

Name Type Description Default
images list[Image]

The images to be removed from the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the given images removed

Source code in src/safeds/data/image/containers/_image_list.py
def remove_images(self, images: list[Image]) -> ImageList:
    """
    Return a new `ImageList` with the given images removed from the image list.

    If one image has multiple occurrences, all occurrences will be removed.

    The original image list is not modified.

    Parameters
    ----------
    images:
        The images to be removed from the image list

    Returns
    -------
    image_list:
        the image list with the given images removed
    """
    indices_to_remove = []
    for image in images:
        indices_to_remove += self.index(image)
    return self._remove_image_by_index_ignore_invalid(list(set(indices_to_remove)))

remove_images_with_size

Return a new ImageList with the all images of the given size removed.

The original image list is not modified.

Parameters:

Name Type Description Default
width int

The width of the images to be removed from the image list

required
height int

The height of the images to be removed from the image list

required

Returns:

Name Type Description
image_list ImageList

the image list with the given images removed

Raises:

Type Description
OutOfBoundsError

If width or height are below 1

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def remove_images_with_size(self, width: int, height: int) -> ImageList:
    """
    Return a new `ImageList` with the all images of the given size removed.

    The original image list is not modified.

    Parameters
    ----------
    width:
        The width of the images to be removed from the image list
    height:
        The height of the images to be removed from the image list

    Returns
    -------
    image_list:
        the image list with the given images removed

    Raises
    ------
    OutOfBoundsError
        If width or height are below 1
    """

resize

Return a new ImageList with all images resized to a given size.

The original image list is not modified.

Parameters:

Name Type Description Default
new_width int

the new width of the images

required
new_height int

the new height of the images

required

Returns:

Name Type Description
image_list ImageList

The image list with all images resized to the given width and height.

Raises:

Type Description
OutOfBoundsError

If new_width or new_height are below 1

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def resize(self, new_width: int, new_height: int) -> ImageList:
    """
    Return a new `ImageList` with all images resized to a given size.

    The original image list is not modified.

    Parameters
    ----------
    new_width:
        the new width of the images
    new_height:
        the new height of the images

    Returns
    -------
    image_list:
        The image list with all images resized to the given width and height.

    Raises
    ------
    OutOfBoundsError
        If new_width or new_height are below 1
    """

rotate_left

Return a new ImageList where all images have been rotated 90 degrees counter-clockwise.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with all images rotated

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def rotate_left(self) -> ImageList:
    """
    Return a new `ImageList` where all images have been rotated 90 degrees counter-clockwise.

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with all images rotated
    """

rotate_right

Return a new ImageList where all images have been rotated 90 degrees clockwise.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

The image list with all images rotated

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def rotate_right(self) -> ImageList:
    """
    Return a new `ImageList` where all images have been rotated 90 degrees clockwise.

    The original image list is not modified.

    Returns
    -------
    image_list:
        The image list with all images rotated
    """

sharpen

Return a new ImageList where all images have been sharpened.

The original image list is not modified.

Parameters:

Name Type Description Default
factor float

If factor > 1, increase the sharpness of the images. If factor = 1, no changes will be made. If factor < 1, blur the images. Has to be bigger than or equal to 0 (blurred).

required

Returns:

Name Type Description
image_list ImageList

The image list with sharpened images

Raises:

Type Description
OutOfBoundsError

If factor is smaller than 0.

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def sharpen(self, factor: float) -> ImageList:
    """
    Return a new `ImageList` where all images have been sharpened.

    The original image list is not modified.

    Parameters
    ----------
    factor:
        If factor > 1, increase the sharpness of the images.
        If factor = 1, no changes will be made.
        If factor < 1, blur the images.
        Has to be bigger than or equal to 0 (blurred).

    Returns
    -------
    image_list:
        The image list with sharpened images

    Raises
    ------
    OutOfBoundsError
        If factor is smaller than 0.
    """

shuffle_images

Return a new ImageList with all images shuffled.

The original image list is not modified.

Returns:

Name Type Description
image_list ImageList

the image list with shuffled images

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def shuffle_images(self) -> ImageList:
    """
    Return a new `ImageList` with all images shuffled.

    The original image list is not modified.

    Returns
    -------
    image_list:
        the image list with shuffled images
    """

to_images

Return a list of all images in this image list.

Parameters:

Name Type Description Default
indices list[int] | None

a list of all indices to include in the output. If None, all indices will be included

None

Returns:

Name Type Description
images list[Image]

the list of all images

Raises:

Type Description
IndexOutOfBoundsError

If any index is out of bounds

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def to_images(self, indices: list[int] | None = None) -> list[Image]:
    """
    Return a list of all images in this image list.

    Parameters
    ----------
    indices:
        a list of all indices to include in the output. If None, all indices will be included

    Returns
    -------
    images:
        the list of all images

    Raises
    ------
    IndexOutOfBoundsError
        If any index is out of bounds
    """

to_jpeg_files

Save all images as jpeg files.

Parameters:

Name Type Description Default
path str | Path | list[str | Path]

Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

required

Raises:

Type Description
IllegalFormatError

If the channel of the images is not supported

ValueError

If the path is a list but has too few or too many entries

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def to_jpeg_files(self, path: str | Path | list[str | Path]) -> None:
    """
    Save all images as jpeg files.

    Parameters
    ----------
    path:
        Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

    Raises
    ------
    IllegalFormatError
        If the channel of the images is not supported
    ValueError
        If the path is a list but has too few or too many entries
    """

to_png_files

Save all images as png files.

Parameters:

Name Type Description Default
path str | Path | list[str | Path]

Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

required

Raises:

Type Description
ValueError

If the path is a list but has too few or too many entries

Source code in src/safeds/data/image/containers/_image_list.py
@abstractmethod
def to_png_files(self, path: str | Path | list[str | Path]) -> None:
    """
    Save all images as png files.

    Parameters
    ----------
    path:
        Either the path to a directory or a list of directories which has directories for either all different sizes or all different images. Any non-existant path will be created

    Raises
    ------
    ValueError
        If the path is a list but has too few or too many entries
    """